diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index eab35b9896fbb..003dbe3fd22f6 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -14,7 +14,9 @@ disabled: - x-pack/test/api_integration/config.ts - x-pack/test/fleet_api_integration/config.base.ts - x-pack/test/security_solution_api_integration/config/ess/config.base.ts + - x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts - x-pack/test/security_solution_api_integration/config/serverless/config.base.ts + - x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.ts - x-pack/test/security_solution_endpoint/config.base.ts - x-pack/test/security_solution_endpoint_api_int/config.base.ts @@ -156,7 +158,9 @@ enabled: - test/server_integration/http/ssl_with_p12/config.js - test/server_integration/http/ssl/config.js - test/ui_capabilities/newsfeed_err/config.ts - - x-pack/test/accessibility/config.ts + - x-pack/test/accessibility/apps/group1/config.ts + - x-pack/test/accessibility/apps/group2/config.ts + - x-pack/test/accessibility/apps/group3/config.ts - x-pack/test/localization/config.ja_jp.ts - x-pack/test/localization/config.fr_fr.ts - x-pack/test/localization/config.zh_cn.ts @@ -329,7 +333,6 @@ enabled: - x-pack/test/kubernetes_security/basic/config.ts - x-pack/test/licensing_plugin/config.public.ts - x-pack/test/licensing_plugin/config.ts - - x-pack/test/lists_api_integration/security_and_spaces/config.ts - x-pack/test/monitoring_api_integration/config.ts - x-pack/test/observability_api_integration/basic/config.ts - x-pack/test/observability_api_integration/trial/config.ts @@ -486,3 +489,11 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/ess.config.ts + + diff --git a/.buildkite/pipeline-utils/ci-stats/client.ts b/.buildkite/pipeline-utils/ci-stats/client.ts index 6a5c048464c70..d7367b89947ad 100644 --- a/.buildkite/pipeline-utils/ci-stats/client.ts +++ b/.buildkite/pipeline-utils/ci-stats/client.ts @@ -201,7 +201,7 @@ export class CiStatsClient { headers: this.defaultHeaders, }); } catch (error) { - console.error('CI Stats request error:', error); + console.error('CI Stats request error:', error?.response?.data?.message); if (attempt < maxAttempts) { const sec = attempt * 3; @@ -210,7 +210,7 @@ export class CiStatsClient { continue; } - throw error; + throw new Error('Failed to connect to CI Stats.'); } } } diff --git a/.buildkite/pipeline-utils/ci-stats/on_complete.ts b/.buildkite/pipeline-utils/ci-stats/on_complete.ts index 0b93fd6b0673f..ea95442dab882 100644 --- a/.buildkite/pipeline-utils/ci-stats/on_complete.ts +++ b/.buildkite/pipeline-utils/ci-stats/on_complete.ts @@ -28,7 +28,15 @@ export async function onComplete() { const report = await ciStats.getPrReport(process.env.CI_STATS_BUILD_ID); if (report?.md) { - buildkite.setMetadata('pr_comment:ci_stats_report:body', report.md); + // buildkite has a metadata size limit of 100kb, so we only add this, if it's small enough + if (new Blob([report.md]).size < 100000) { + buildkite.setMetadata('pr_comment:ci_stats_report:body', report.md); + } else { + buildkite.setMetadata( + 'pr_comment:ci_stats_report:body', + 'The CI Stats report is too large to be displayed here, check out the CI build annotation for this information.' + ); + } const annotationType = report?.success ? 'info' : 'error'; buildkite.setAnnotation('ci-stats-report', annotationType, report.md); diff --git a/.buildkite/pipelines/artifacts.yml b/.buildkite/pipelines/artifacts.yml index 4a52ee7402823..64ebe01e9227b 100644 --- a/.buildkite/pipelines/artifacts.yml +++ b/.buildkite/pipelines/artifacts.yml @@ -61,7 +61,7 @@ steps: - exit_status: '*' limit: 1 - - command: KIBANA_DOCKER_CONTEXT=ubi9 .buildkite/scripts/steps/artifacts/docker_context.sh + - command: KIBANA_DOCKER_CONTEXT=ubi .buildkite/scripts/steps/artifacts/docker_context.sh label: 'Docker Context Verification' agents: queue: n2-2 diff --git a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml index 8e64513b14900..84642f08816f8 100644 --- a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml +++ b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml @@ -56,7 +56,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-serverless-security-cypress + MSG: "--- Security Serverless Cypress Tests" label: 'Serverless Security Cypress Tests' if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: @@ -69,7 +74,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_explore.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:explore:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Explore - Security Solution Cypress Tests" label: 'Serverless Explore - Security Solution Cypress Tests' if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: @@ -82,7 +92,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_investigations.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:investigations:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Investigations Cypress Tests on Serverless" label: 'Serverless Investigations - Security Solution Cypress Tests' if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: @@ -95,7 +110,48 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management Cypress Tests on Serverless" + label: 'Serverless Rule Management - Security Solution Cypress Tests' + if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:prebuilt_rules:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Prebuilt Rules - Cypress Tests on Serverless" + label: 'Serverless Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:dw:serverless:run + ROOT_DIR: x-pack/plugins/security_solution + JOB_TITLE: kibana-defend-workflows-serverless-cypress + MSG: "--- Defend Workflows Cypress tests on Serverless" label: 'Defend Workflows Cypress Tests on Serverless' if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index 8b00db428a713..622fd50cd58ff 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -79,7 +79,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-serverless-security-cypress + MSG: "--- Security Serverless Cypress Tests" label: 'Serverless Security Cypress Tests' agents: queue: n2-4-spot @@ -91,8 +96,13 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_explore.sh - label: 'Serverless Explore - Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:explore:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Explore - Security Cypress Tests" + label: 'Serverless Explore - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -103,8 +113,30 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_investigations.sh - label: 'Serverless Investigations - Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:investigations:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Investigations - Security Cypress Tests" + label: 'Serverless Investigations - Security Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Rule Management - Security Cypress Tests " + label: 'Serverless Rule Management - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -115,7 +147,63 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:prebuilt_rules:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Prebuilt Rules - Security Cypress Tests" + label: 'Rule Management - Prebuilt Rules - Security Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Security Solution Cypress Tests" + label: 'Rule Management - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:prebuilt_rules:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Prebuilt Rules - Security Solution Cypress Tests" + label: 'Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Security Solution Cypress tests (Chrome)" label: 'Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -127,7 +215,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution_explore.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:explore:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Explore Cypress Tests on Security Solution" label: 'Explore - Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -139,7 +232,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution_investigations.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:investigations:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Investigations - Security Solution Cypress Tests" label: 'Investigations - Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -151,7 +249,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:dw:run + ROOT_DIR: x-pack/plugins/security_solution + JOB_TITLE: kibana-defend-workflows-cypress + MSG: "--- Defend Workflows Cypress tests" label: 'Defend Workflows Cypress Tests' agents: queue: n2-4-virt @@ -163,7 +266,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:dw:serverless:run + ROOT_DIR: x-pack/plugins/security_solution + JOB_TITLE: kibana-defend-workflows-serverless-cypress + MSG: "--- Defend Workflows Cypress tests on Serverless" label: 'Defend Workflows Cypress Tests on Serverless' agents: queue: n2-4-virt @@ -175,7 +283,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/threat_intelligence.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run + ROOT_DIR: x-pack/plugins/threat_intelligence + JOB_TITLE: kibana-threat-intelligence-chrome + MSG: "--- Threat Intelligence Cypress tests (Chrome)" label: 'Threat Intelligence Cypress Tests' agents: queue: n2-4-spot diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 49215bbd00f11..b4b7c83ca52eb 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -57,7 +57,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-serverless-security-cypress + MSG: "--- Security Serverless Cypress Tests" label: 'Serverless Security Cypress Tests' agents: queue: n2-4-spot @@ -69,8 +74,13 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_explore.sh - label: 'Serverless Explore - Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:explore:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Explore - Security Cypress Tests" + label: 'Serverless Explore - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -81,8 +91,30 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless_investigations.sh - label: 'Serverless Investigations - Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:investigations:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Investigations - Security Cypress Tests" + label: 'Serverless Investigations - Security Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Serverless Rule Management - Security Cypress Tests " + label: 'Serverless Rule Management - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -93,7 +125,29 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:prebuilt_rules:run:serverless + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Prebuilt Rules - Security Cypress Tests" + label: 'Rule Management - Prebuilt Rules - Security Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Security Solution Cypress tests (Chrome)" label: 'Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -105,7 +159,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution_explore.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:explore:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Explore Cypress Tests on Security Solution" label: 'Explore - Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -117,7 +176,46 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution_investigations.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Security Solution Cypress Tests" + label: 'Rule Management - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:rule_management:prebuilt_rules:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Rule Management - Prebuilt Rules - Security Solution Cypress Tests" + label: 'Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:investigations:run:ess + ROOT_DIR: x-pack/test/security_solution_cypress + JOB_TITLE: kibana-security-solution-chrome + MSG: "--- Investigations - Security Solution Cypress Tests" label: 'Investigations - Security Solution Cypress Tests' agents: queue: n2-4-spot @@ -129,7 +227,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:dw:run + ROOT_DIR: x-pack/plugins/security_solution + JOB_TITLE: kibana-defend-workflows-cypress + MSG: "--- Defend Workflows Cypress tests" label: 'Defend Workflows Cypress Tests' agents: queue: n2-4-virt @@ -141,7 +244,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:dw:serverless:run + ROOT_DIR: x-pack/plugins/security_solution + JOB_TITLE: kibana-defend-workflows-serverless-cypress + MSG: "--- Defend Workflows Cypress tests on Serverless" label: 'Defend Workflows Cypress Tests on Serverless' agents: queue: n2-4-virt @@ -153,7 +261,12 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/threat_intelligence.sh + - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + env: + TARGET: cypress:run + ROOT_DIR: x-pack/plugins/threat_intelligence + JOB_TITLE: kibana-threat-intelligence-chrome + MSG: "--- Threat Intelligence Cypress tests (Chrome)" label: 'Threat Intelligence Cypress Tests' agents: queue: n2-4-spot @@ -190,7 +303,12 @@ steps: limit: 1 # status_exception: Native role management is not enabled in this Elasticsearch instance - # - command: .buildkite/scripts/steps/functional/security_serverless_defend_workflows.sh + # - command: .buildkite/scripts/steps/functional/security_cypress_exec.sh + # env: + # TARGET: cypress:run + # ROOT_DIR: x-pack/test_serverless/functional/test_suites/security/cypress + # JOB_TITLE: kibana-serverless-security-cypress + # MSG: "--- Security Defend Workflows Serverless Cypress" # label: 'Serverless Security Defend Workflows Cypress Tests' # agents: # queue: n2-4-spot diff --git a/.buildkite/pipelines/quality-gates/pipeline.tests-production.yaml b/.buildkite/pipelines/quality-gates/pipeline.tests-production.yaml index 3e8a2358a9d3c..7cdc07ec861f2 100644 --- a/.buildkite/pipelines/quality-gates/pipeline.tests-production.yaml +++ b/.buildkite/pipelines/quality-gates/pipeline.tests-production.yaml @@ -11,6 +11,8 @@ steps: TARGET_ENV: production CHECK_SLO: true CHECK_SLO_TAG: kibana + CHECK_SLO_WAITING_PERIOD: 15m + CHECK_SLO_BURN_RATE_THRESHOLD: 0.1 soft_fail: true - label: ":rocket: control-plane e2e tests" diff --git a/.buildkite/pipelines/security_solution/security_solution_cypress.yml b/.buildkite/pipelines/security_solution/security_solution_cypress.yml index 247505ef1c85a..77e7fea574352 100644 --- a/.buildkite/pipelines/security_solution/security_solution_cypress.yml +++ b/.buildkite/pipelines/security_solution/security_solution_cypress.yml @@ -30,6 +30,30 @@ steps: # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. timeout_in_minutes: 300 parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management + label: 'Serverless MKI QA Rule Management - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + timeout_in_minutes: 300 + parallelism: 8 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/pipelines/security_solution_quality_gate/security_solution_cypress/mki_security_solution_cypress.sh cypress:run:qa:serverless:rule_management:prebuilt_rules + label: 'Serverless MKI QA Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + agents: + queue: n2-4-spot + # TODO : Revise the timeout when the pipeline will be officially integrated with the quality gate. + timeout_in_minutes: 300 + parallelism: 6 retry: automatic: - exit_status: '*' diff --git a/.buildkite/scripts/lifecycle/pre_build.sh b/.buildkite/scripts/lifecycle/pre_build.sh index 62f94dd493fa8..b8ccaf04f9bb9 100755 --- a/.buildkite/scripts/lifecycle/pre_build.sh +++ b/.buildkite/scripts/lifecycle/pre_build.sh @@ -8,8 +8,6 @@ if [[ "${GITHUB_BUILD_COMMIT_STATUS_ENABLED:-}" != "true" ]]; then "$(dirname "${0}")/commit_status_start.sh" fi -export CI_STATS_TOKEN="$(retry 5 5 vault read -field=api_token secret/kibana-issues/dev/kibana_ci_stats)" -export CI_STATS_HOST="$(retry 5 5 vault read -field=api_host secret/kibana-issues/dev/kibana_ci_stats)" ts-node "$(dirname "${0}")/ci_stats_start.ts" diff --git a/.buildkite/scripts/lifecycle/pre_command.sh b/.buildkite/scripts/lifecycle/pre_command.sh index 9cfa973b28024..965c09621caa0 100755 --- a/.buildkite/scripts/lifecycle/pre_command.sh +++ b/.buildkite/scripts/lifecycle/pre_command.sh @@ -77,16 +77,16 @@ EOF { CI_STATS_BUILD_ID="$(buildkite-agent meta-data get ci_stats_build_id --default '')" export CI_STATS_BUILD_ID + + CI_STATS_TOKEN="$(retry 5 5 vault read -field=api_token secret/kibana-issues/dev/kibana_ci_stats)" + export CI_STATS_TOKEN + + CI_STATS_HOST="$(retry 5 5 vault read -field=api_host secret/kibana-issues/dev/kibana_ci_stats)" + export CI_STATS_HOST if [[ "$CI_STATS_BUILD_ID" ]]; then echo "CI Stats Build ID: $CI_STATS_BUILD_ID" - CI_STATS_TOKEN="$(retry 5 5 vault read -field=api_token secret/kibana-issues/dev/kibana_ci_stats)" - export CI_STATS_TOKEN - - CI_STATS_HOST="$(retry 5 5 vault read -field=api_host secret/kibana-issues/dev/kibana_ci_stats)" - export CI_STATS_HOST - KIBANA_CI_STATS_CONFIG=$(jq -n \ --arg buildId "$CI_STATS_BUILD_ID" \ --arg apiUrl "https://$CI_STATS_HOST" \ diff --git a/.buildkite/scripts/packer_cache.sh b/.buildkite/scripts/packer_cache.sh index 727accaeead8f..66d663180ec3c 100755 --- a/.buildkite/scripts/packer_cache.sh +++ b/.buildkite/scripts/packer_cache.sh @@ -14,5 +14,9 @@ for version in $(cat versions.json | jq -r '.versions[].version'); do node scripts/es snapshot --download-only --base-path "$ES_CACHE_DIR" --version "$version" done +for version in $(cat versions.json | jq -r '.versions[].version'); do + node x-pack/plugins/security_solution/scripts/endpoint/agent_downloader --version "$version" +done + echo "--- Cloning repos for docs build" node scripts/validate_next_docs --clone-only diff --git a/.buildkite/scripts/steps/artifacts/docker_context.sh b/.buildkite/scripts/steps/artifacts/docker_context.sh index b6fe4834465fc..de90621ada2d9 100755 --- a/.buildkite/scripts/steps/artifacts/docker_context.sh +++ b/.buildkite/scripts/steps/artifacts/docker_context.sh @@ -26,8 +26,9 @@ case $KIBANA_DOCKER_CONTEXT in ubi8) DOCKER_CONTEXT_FILE="kibana-ubi8-$FULL_VERSION-docker-build-context.tar.gz" ;; - ubi9) - DOCKER_CONTEXT_FILE="kibana-ubi9-$FULL_VERSION-docker-build-context.tar.gz" + ubi) + # Currently ubi9. After ubi8 we're moving to a version agnostic filename + DOCKER_CONTEXT_FILE="kibana-ubi-$FULL_VERSION-docker-build-context.tar.gz" ;; ironbank) DOCKER_CONTEXT_FILE="kibana-ironbank-$FULL_VERSION-docker-build-context.tar.gz" diff --git a/.buildkite/scripts/steps/cloud/purge_deployments.ts b/.buildkite/scripts/steps/cloud/purge_deployments.ts index a166f09b73d6a..845e6740cd48e 100644 --- a/.buildkite/scripts/steps/cloud/purge_deployments.ts +++ b/.buildkite/scripts/steps/cloud/purge_deployments.ts @@ -23,12 +23,11 @@ const DAY_IN_SECONDS = 60 * 60 * 24; for (const deployment of prDeployments) { try { const prNumber = deployment.name.match(/^kibana-pr-([0-9]+)$/)[1]; - const prJson = execSync(`gh pr view '${prNumber}' --json state,labels,commits`).toString(); + const prJson = execSync(`gh pr view '${prNumber}' --json state,labels,updatedAt`).toString(); const pullRequest = JSON.parse(prJson); const prOpen = pullRequest.state === 'OPEN'; - const lastCommit = pullRequest.commits.slice(-1)[0]; - const lastCommitTimestamp = new Date(lastCommit.committedDate).getTime() / 1000; + const lastCommitTimestamp = new Date(pullRequest.updatedAt).getTime() / 1000; const persistDeployment = Boolean( pullRequest.labels.filter((label: any) => label.name === 'ci:cloud-persist-deployment').length diff --git a/.buildkite/scripts/steps/cloud/purge_projects.ts b/.buildkite/scripts/steps/cloud/purge_projects.ts index f89aa65fa855a..c3c427c6a3885 100644 --- a/.buildkite/scripts/steps/cloud/purge_projects.ts +++ b/.buildkite/scripts/steps/cloud/purge_projects.ts @@ -66,12 +66,11 @@ async function purgeProjects() { const NOW = new Date().getTime() / 1000; const DAY_IN_SECONDS = 60 * 60 * 24; const prJson = execSync( - `gh pr view '${project.prNumber}' --json state,labels,commits` + `gh pr view '${project.prNumber}' --json state,labels,updatedAt` ).toString(); const pullRequest = JSON.parse(prJson); const prOpen = pullRequest.state === 'OPEN'; - const lastCommit = pullRequest.commits.slice(-1)[0]; - const lastCommitTimestamp = new Date(lastCommit.committedDate).getTime() / 1000; + const lastCommitTimestamp = new Date(pullRequest.updatedAt).getTime() / 1000; const persistDeployment = Boolean( pullRequest.labels.filter((label: any) => label.name === 'ci:project-persist-deployment') diff --git a/.buildkite/scripts/steps/functional/defend_workflows.sh b/.buildkite/scripts/steps/functional/defend_workflows.sh deleted file mode 100755 index c85321869f836..0000000000000 --- a/.buildkite/scripts/steps/functional/defend_workflows.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-defend-workflows-cypress -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Defend Workflows Cypress tests" - -cd x-pack/plugins/security_solution - -set +e -yarn cypress:dw:run; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/defend_workflows_serverless.sh b/.buildkite/scripts/steps/functional/defend_workflows_serverless.sh deleted file mode 100755 index 15e803eb87646..0000000000000 --- a/.buildkite/scripts/steps/functional/defend_workflows_serverless.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-defend-workflows-serverless-cypress -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Defend Workflows Cypress tests on Serverless" - -cd x-pack/plugins/security_solution - -set +e -yarn cypress:dw:serverless:run; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_cypress_exec.sh b/.buildkite/scripts/steps/functional/security_cypress_exec.sh new file mode 100644 index 0000000000000..90c94f1661523 --- /dev/null +++ b/.buildkite/scripts/steps/functional/security_cypress_exec.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/steps/functional/common.sh +source .buildkite/scripts/steps/functional/common_cypress.sh + +export JOB=$JOB_TITLE +export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} + +echo $MSG + +cd $ROOT_DIR + +TARGETS_ARRAY=$(jq .scripts package.json | jq 'keys') +if [[ " ${TARGETS_ARRAY[*]} " == *"$TARGET"* ]]; then + echo "Target '$TARGET' exists in the available targets in package.json" + echo "Proceeding in test run!" +else + echo "The provided target '$TARGET' could not be found in the available targets in package.json" + echo "Abort the test runtime due to unexpected target script" + exit 1 +fi + +set +e +yarn $TARGET; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_serverless.sh b/.buildkite/scripts/steps/functional/security_serverless.sh deleted file mode 100644 index 9903f44da0373..0000000000000 --- a/.buildkite/scripts/steps/functional/security_serverless.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-serverless-security-cypress -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Security Serverless Cypress Tests" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:run:serverless; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_serverless_defend_workflows.sh b/.buildkite/scripts/steps/functional/security_serverless_defend_workflows.sh deleted file mode 100644 index 323f1fc2224f1..0000000000000 --- a/.buildkite/scripts/steps/functional/security_serverless_defend_workflows.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-serverless-security-cypress -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Security Defend Workflows Serverless Cypress" - -yarn --cwd x-pack/test_serverless/functional/test_suites/security/cypress cypress:run \ No newline at end of file diff --git a/.buildkite/scripts/steps/functional/security_serverless_explore.sh b/.buildkite/scripts/steps/functional/security_serverless_explore.sh deleted file mode 100644 index 52237e4d8f902..0000000000000 --- a/.buildkite/scripts/steps/functional/security_serverless_explore.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-security-solution-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Explore - Security Solution Cypress Tests" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:explore:run:serverless; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_serverless_investigations.sh b/.buildkite/scripts/steps/functional/security_serverless_investigations.sh deleted file mode 100644 index 6a79a92871fb2..0000000000000 --- a/.buildkite/scripts/steps/functional/security_serverless_investigations.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-security-solution-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Investigations Cypress Tests on Serverless" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:investigations:run:serverless; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_solution.sh b/.buildkite/scripts/steps/functional/security_solution.sh deleted file mode 100755 index fdba86f4dee4e..0000000000000 --- a/.buildkite/scripts/steps/functional/security_solution.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-security-solution-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Security Solution Cypress tests (Chrome)" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:run:ess; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_solution_explore.sh b/.buildkite/scripts/steps/functional/security_solution_explore.sh deleted file mode 100644 index e5513e63a7664..0000000000000 --- a/.buildkite/scripts/steps/functional/security_solution_explore.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-security-solution-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Explore Cypress Tests on Security Solution" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:explore:run:ess; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_solution_investigations.sh b/.buildkite/scripts/steps/functional/security_solution_investigations.sh deleted file mode 100644 index d26261b638d5a..0000000000000 --- a/.buildkite/scripts/steps/functional/security_solution_investigations.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-security-solution-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Investigations - Security Solution Cypress Tests" - -cd x-pack/test/security_solution_cypress - -set +e -yarn cypress:investigations:run:ess; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/threat_intelligence.sh b/.buildkite/scripts/steps/functional/threat_intelligence.sh deleted file mode 100755 index 0c2c80942e7c6..0000000000000 --- a/.buildkite/scripts/steps/functional/threat_intelligence.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/steps/functional/common.sh -source .buildkite/scripts/steps/functional/common_cypress.sh - -export JOB=kibana-threat-intelligence-chrome -export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} - -echo "--- Threat Intelligence Cypress tests (Chrome)" - -yarn --cwd x-pack/plugins/threat_intelligence cypress:run diff --git a/.buildkite/scripts/steps/serverless/build_and_deploy.sh b/.buildkite/scripts/steps/serverless/build_and_deploy.sh index 6c5b72a0b4922..dba1103e3a916 100644 --- a/.buildkite/scripts/steps/serverless/build_and_deploy.sh +++ b/.buildkite/scripts/steps/serverless/build_and_deploy.sh @@ -105,3 +105,5 @@ EOF is_pr_with_label "ci:project-deploy-elasticsearch" && deploy "elasticsearch" is_pr_with_label "ci:project-deploy-observability" && deploy "observability" is_pr_with_label "ci:project-deploy-security" && deploy "security" + +exit 0; diff --git a/.eslintrc.js b/.eslintrc.js index 82193a7e2ecb7..23706483a4426 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -941,7 +941,7 @@ module.exports = { ], rules: { '@kbn/i18n/strings_should_be_translated_with_i18n': 'warn', - '@kbn/i18n/strings_should_be_translated_with_formatted_message': 'warn', + '@kbn/i18n/i18n_translate_should_start_with_the_right_id': 'warn', }, }, { @@ -1539,6 +1539,17 @@ module.exports = { }, }, + /** + * Serverless Search overrides + */ + { + // All files + files: ['x-pack/plugins/serverless_search/**/*.{ts,tsx}', 'packages/kbn-search-*'], + rules: { + '@kbn/telemetry/event_generating_elements_should_be_instrumented': 'error', + }, + }, + /** * Canvas overrides */ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6a74ffd328196..a366324620ab0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -55,6 +55,7 @@ packages/kbn-bazel-runner @elastic/kibana-operations examples/bfetch_explorer @elastic/appex-sharedux src/plugins/bfetch @elastic/appex-sharedux packages/kbn-calculate-auto @elastic/obs-ux-management-team +packages/kbn-calculate-width-from-char-count @elastic/kibana-visualizations x-pack/plugins/canvas @elastic/kibana-presentation x-pack/test/cases_api_integration/common/plugins/cases @elastic/response-ops packages/kbn-cases-components @elastic/response-ops @@ -368,7 +369,7 @@ src/plugins/es_ui_shared @elastic/platform-deployment-management packages/kbn-eslint-config @elastic/kibana-operations packages/kbn-eslint-plugin-disable @elastic/kibana-operations packages/kbn-eslint-plugin-eslint @elastic/kibana-operations -packages/kbn-eslint-plugin-i18n @elastic/obs-knowledge-team +packages/kbn-eslint-plugin-i18n @elastic/obs-knowledge-team @elastic/kibana-operations packages/kbn-eslint-plugin-imports @elastic/kibana-operations packages/kbn-eslint-plugin-telemetry @elastic/obs-knowledge-team x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security @@ -516,8 +517,10 @@ x-pack/packages/ml/agg_utils @elastic/ml-ui x-pack/packages/ml/anomaly_utils @elastic/ml-ui x-pack/packages/ml/category_validator @elastic/ml-ui x-pack/packages/ml/chi2test @elastic/ml-ui +x-pack/packages/ml/creation_wizard_utils @elastic/ml-ui x-pack/packages/ml/data_frame_analytics_utils @elastic/ml-ui x-pack/packages/ml/data_grid @elastic/ml-ui +x-pack/packages/ml/data_view_utils @elastic/ml-ui x-pack/packages/ml/date_picker @elastic/ml-ui x-pack/packages/ml/date_utils @elastic/ml-ui x-pack/packages/ml/error_utils @elastic/ml-ui @@ -538,6 +541,7 @@ x-pack/packages/ml/string_hash @elastic/ml-ui x-pack/packages/ml/trained_models_utils @elastic/ml-ui x-pack/packages/ml/ui_actions @elastic/ml-ui x-pack/packages/ml/url_state @elastic/ml-ui +packages/kbn-mock-idp-plugin @elastic/kibana-security packages/kbn-monaco @elastic/appex-sharedux x-pack/plugins/monitoring_collection @elastic/obs-ux-infra_services-team x-pack/plugins/monitoring @elastic/obs-ux-infra_services-team @@ -557,6 +561,7 @@ x-pack/plugins/observability @elastic/obs-ux-management-team x-pack/plugins/observability_shared @elastic/observability-ui x-pack/test/security_api_integration/plugins/oidc_provider @elastic/kibana-security test/common/plugins/otel_metrics @elastic/obs-ux-infra_services-team +packages/kbn-openapi-bundler @elastic/security-detection-rule-management packages/kbn-openapi-generator @elastic/security-detection-rule-management packages/kbn-optimizer @elastic/kibana-operations packages/kbn-optimizer-webpack-helpers @elastic/kibana-operations @@ -564,6 +569,7 @@ packages/kbn-osquery-io-ts-types @elastic/security-asset-management x-pack/plugins/osquery @elastic/security-defend-workflows examples/partial_results_example @elastic/kibana-data-discovery x-pack/plugins/painless_lab @elastic/platform-deployment-management +packages/kbn-panel-loader @elastic/kibana-presentation packages/kbn-peggy @elastic/kibana-operations packages/kbn-peggy-loader @elastic/kibana-operations packages/kbn-performance-testing-dataset-extractor @elastic/kibana-performance-testing @@ -642,6 +648,9 @@ packages/kbn-search-response-warnings @elastic/kibana-data-discovery x-pack/plugins/searchprofiler @elastic/platform-deployment-management x-pack/test/security_api_integration/packages/helpers @elastic/kibana-security x-pack/plugins/security @elastic/kibana-security +x-pack/packages/security/plugin_types_common @elastic/kibana-security +x-pack/packages/security/plugin_types_public @elastic/kibana-security +x-pack/packages/security/plugin_types_server @elastic/kibana-security x-pack/plugins/security_solution_ess @elastic/security-solution x-pack/packages/security-solution/features @elastic/security-threat-hunting-explore x-pack/test/cases_api_integration/common/plugins/security_solution @elastic/response-ops @@ -752,7 +761,6 @@ test/server_integration/plugins/status_plugin_b @elastic/kibana-core packages/kbn-std @elastic/kibana-core packages/kbn-stdio-dev-helpers @elastic/kibana-operations packages/kbn-storybook @elastic/kibana-operations -packages/kbn-subscription-tracking @elastic/security-threat-hunting-investigations x-pack/plugins/synthetics @elastic/obs-ux-infra_services-team x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture @elastic/response-ops x-pack/test/plugin_api_perf/plugins/task_manager_performance @elastic/response-ops @@ -868,7 +876,7 @@ packages/kbn-zod-helpers @elastic/security-detection-rule-management /test/functional/apps/management/ccs_compatibility/_data_views_ccs.ts @elastic/kibana-data-discovery /test/functional/apps/management/data_views @elastic/kibana-data-discovery /test/plugin_functional/test_suites/data_plugin @elastic/kibana-data-discovery -/x-pack/test/accessibility/apps/search_sessions.ts @elastic/kibana-data-discovery +/x-pack/test/accessibility/apps/group3/search_sessions.ts @elastic/kibana-data-discovery /x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @elastic/kibana-data-discovery /x-pack/test/api_integration/apis/search @elastic/kibana-data-discovery /x-pack/test/examples/search_examples @elastic/kibana-data-discovery @@ -1071,8 +1079,8 @@ x-pack/plugins/infra/server/lib/alerting @elastic/obs-ux-management-team #CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation # Machine Learning -/x-pack/test/accessibility/apps/ml.ts @elastic/ml-ui -/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts @elastic/ml-ui +/x-pack/test/accessibility/apps/group2/ml.ts @elastic/ml-ui +/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts @elastic/ml-ui /x-pack/test/api_integration/apis/ml/ @elastic/ml-ui /x-pack/test/api_integration_basic/apis/ml/ @elastic/ml-ui /x-pack/test/functional/apps/ml/ @elastic/ml-ui @@ -1086,7 +1094,7 @@ x-pack/plugins/infra/server/lib/alerting @elastic/obs-ux-management-team /x-pack/test/screenshot_creation/services/ml_screenshots.ts @elastic/ml-ui # Additional plugins and packages maintained by the ML team. -/x-pack/test/accessibility/apps/transform.ts @elastic/ml-ui +/x-pack/test/accessibility/apps/group2/transform.ts @elastic/ml-ui /x-pack/test/api_integration/apis/aiops/ @elastic/ml-ui /x-pack/test/api_integration/apis/transform/ @elastic/ml-ui /x-pack/test/api_integration_basic/apis/transform/ @elastic/ml-ui @@ -1173,10 +1181,10 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /test/interactive_setup_api_integration/ @elastic/kibana-security /test/interactive_setup_functional/ @elastic/kibana-security /test/plugin_functional/test_suites/core_plugins/rendering.ts @elastic/kibana-security -/x-pack/test/accessibility/apps/login_page.ts @elastic/kibana-security -/x-pack/test/accessibility/apps/roles.ts @elastic/kibana-security -/x-pack/test/accessibility/apps/spaces.ts @elastic/kibana-security -/x-pack/test/accessibility/apps/users.ts @elastic/kibana-security +/x-pack/test/accessibility/apps/group1/login_page.ts @elastic/kibana-security +/x-pack/test/accessibility/apps/group1/roles.ts @elastic/kibana-security +/x-pack/test/accessibility/apps/group1/spaces.ts @elastic/kibana-security +/x-pack/test/accessibility/apps/group1/users.ts @elastic/kibana-security /x-pack/test/api_integration/apis/security/ @elastic/kibana-security /x-pack/test/api_integration/apis/spaces/ @elastic/kibana-security /x-pack/test/ui_capabilities/ @elastic/kibana-security @@ -1219,7 +1227,6 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /x-pack/test/functional/es_archives/endpoint/ @elastic/security-solution /x-pack/test/plugin_functional/test_suites/resolver/ @elastic/security-solution /x-pack/test/detection_engine_api_integration @elastic/security-solution -/x-pack/test/lists_api_integration @elastic/security-solution /x-pack/test/api_integration/apis/security_solution @elastic/security-solution #CC# /x-pack/plugins/security_solution/ @elastic/security-solution @@ -1318,9 +1325,7 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring @elastic/security-detection-rule-management /x-pack/plugins/security_solution/common/detection_engine/rule_management @elastic/security-detection-rule-management -/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management /x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management -/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details @elastic/security-detection-rule-management /x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management /x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/prebuilt_rules @elastic/security-detection-rule-management @@ -1390,12 +1395,14 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-detection-engine /x-pack/test/security_solution_cypress/cypress/e2e/exceptions @elastic/security-detection-engine /x-pack/test/security_solution_cypress/cypress/e2e/overview @elastic/security-detection-engine -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions @elastic/security-detection-engine -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation @elastic/security-detection-engine -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions @elastic/security-detection-engine -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/alerts @elastic/security-detection-engine -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/alerts @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine @elastic/security-detection-engine /x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists @elastic/security-detection-engine ## Security Threat Intelligence - Under Security Platform /x-pack/plugins/security_solution/public/common/components/threat_match @elastic/security-detection-engine diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5974a1ee9a58a..d07f60cf09253 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,6 +10,7 @@ Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios +- [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 1c1b2742001b5..4f4057935265a 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -21,3 +21,5 @@ - "x-pack/plugins/synthetics/**/*.*" - "x-pack/plugins/ux/**/*.*" - "x-pack/plugins/observability/public/components/shared/exploratory_view/**/*.*" + - "Team:obs-ux-management": + - "x-pack/plugins/observability/**/*.*" diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index fe9d449c3f4fe..68916c4e28216 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-11-22 +date: 2023-11-30 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 9a1e41a0a5b75..def992d9cd6cb 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-11-22 +date: 2023-11-30 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 17bfeb4efbd4d..21af623fb3958 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-11-22 +date: 2023-11-30 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 9505e1de3807c..3e44582dd40b5 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -1388,6 +1388,56 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "alerting", + "id": "def-server.sanitizeBulkErrorResponse", + "type": "Function", + "tags": [], + "label": "sanitizeBulkErrorResponse", + "description": [], + "signature": [ + "(response: ", + "BulkResponse", + " | ", + "TransportResult", + "<", + "BulkResponse", + ", unknown>) => ", + "BulkResponse", + " | ", + "TransportResult", + "<", + "BulkResponse", + ", unknown>" + ], + "path": "x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "alerting", + "id": "def-server.sanitizeBulkErrorResponse.$1", + "type": "CompoundType", + "tags": [], + "label": "response", + "description": [], + "signature": [ + "BulkResponse", + " | ", + "TransportResult", + "<", + "BulkResponse", + ", unknown>" + ], + "path": "x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -5005,9 +5055,9 @@ }, "; getAuditLogger: () => ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.AuditLogger", "text": "AuditLogger" }, @@ -7406,12 +7456,15 @@ "parentPluginId": "alerting", "id": "def-common.MaintenanceWindowSOProperties", "type": "Interface", - "tags": [], + "tags": [ + "deprecated" + ], "label": "MaintenanceWindowSOProperties", "description": [], "path": "x-pack/plugins/alerting/common/maintenance_window.ts", - "deprecated": false, + "deprecated": true, "trackAdoption": false, + "references": [], "children": [ { "parentPluginId": "alerting", @@ -7521,6 +7574,21 @@ "path": "x-pack/plugins/alerting/common/maintenance_window.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.MaintenanceWindowSOProperties.scopedQuery", + "type": "CompoundType", + "tags": [], + "label": "scopedQuery", + "description": [], + "signature": [ + "ScopedQueryAttributes", + " | null | undefined" + ], + "path": "x-pack/plugins/alerting/common/maintenance_window.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -10187,7 +10255,9 @@ "parentPluginId": "alerting", "id": "def-common.MaintenanceWindow", "type": "Type", - "tags": [], + "tags": [ + "deprecated" + ], "label": "MaintenanceWindow", "description": [], "signature": [ @@ -10217,8 +10287,94 @@ "; eventStartTime: string | null; eventEndTime: string | null; id: string; }" ], "path": "x-pack/plugins/alerting/common/maintenance_window.ts", - "deprecated": false, + "deprecated": true, "trackAdoption": false, + "references": [ + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_bulk_get_maintenance_windows.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/types.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts" + }, + { + "plugin": "triggersActionsUi", + "path": "x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/index.mock.ts" + } + ], "initialIsOpen": false }, { @@ -10237,7 +10393,9 @@ "section": "def-common.RRuleParams", "text": "RRuleParams" }, - "; categoryIds?: string[] | null | undefined; }" + "; categoryIds?: string[] | null | undefined; scopedQuery?: ", + "ScopedQueryAttributes", + " | null | undefined; }" ], "path": "x-pack/plugins/alerting/common/maintenance_window.ts", "deprecated": false, @@ -10263,7 +10421,9 @@ "parentPluginId": "alerting", "id": "def-common.MaintenanceWindowSOAttributes", "type": "Type", - "tags": [], + "tags": [ + "deprecated" + ], "label": "MaintenanceWindowSOAttributes", "description": [], "signature": [ @@ -10284,8 +10444,9 @@ } ], "path": "x-pack/plugins/alerting/common/maintenance_window.ts", - "deprecated": false, + "deprecated": true, "trackAdoption": false, + "references": [], "initialIsOpen": false }, { diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 77953e1c16273..50496a9c06dee 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-11-22 +date: 2023-11-30 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 | |-------------------|-----------|------------------------|-----------------| -| 810 | 1 | 779 | 50 | +| 813 | 1 | 782 | 51 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index 1f8e4fbabe130..0dfa84c0e4233 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -597,7 +597,11 @@ "Type", "; endIndex: ", "Type", - "; }>]>; }> | undefined; handler: ({}: ", + "; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", "APMRouteHandlerResources", " & { params: { path: { serviceName: string; }; query: { start: number; end: number; } & { environment: \"ENVIRONMENT_NOT_DEFINED\" | \"ENVIRONMENT_ALL\" | ", "Branded", @@ -617,7 +621,7 @@ "ApmDocumentType", ".TransactionEvent; rollupInterval: ", "RollupInterval", - "; } & { startIndex: number; endIndex: number; }; }; }) => Promise<{ functions: ", + "; } & { startIndex: number; endIndex: number; } & { kuery: string; }; }; }) => Promise<{ functions: ", { "pluginId": "@kbn/profiling-utils", "scope": "common", @@ -699,7 +703,11 @@ "LiteralC", "<", "RollupInterval", - ".None>]>; }>]>; }> | undefined; handler: ({}: ", + ".None>]>; }>, ", + "TypeC", + "<{ kuery: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", "APMRouteHandlerResources", " & { params: { path: { serviceName: string; }; query: { start: number; end: number; } & { environment: \"ENVIRONMENT_NOT_DEFINED\" | \"ENVIRONMENT_ALL\" | ", "Branded", @@ -719,7 +727,7 @@ "ApmDocumentType", ".TransactionEvent; rollupInterval: ", "RollupInterval", - "; }; }; }) => Promise<{ flamegraph: ", + "; } & { kuery: string; }; }; }) => Promise<{ flamegraph: ", { "pluginId": "@kbn/profiling-utils", "scope": "common", @@ -5855,7 +5863,41 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">]>; }>]>; }> | undefined; handler: ({}: ", + ">]>; }>, ", + "TypeC", + "<{ documentType: ", + "UnionC", + "<[", + "LiteralC", + "<", + "ApmDocumentType", + ".ServiceTransactionMetric>, ", + "LiteralC", + "<", + "ApmDocumentType", + ".TransactionMetric>, ", + "LiteralC", + "<", + "ApmDocumentType", + ".TransactionEvent>]>; rollupInterval: ", + "UnionC", + "<[", + "LiteralC", + "<", + "RollupInterval", + ".OneMinute>, ", + "LiteralC", + "<", + "RollupInterval", + ".TenMinutes>, ", + "LiteralC", + "<", + "RollupInterval", + ".SixtyMinutes>, ", + "LiteralC", + "<", + "RollupInterval", + ".None>]>; }>]>; }> | undefined; handler: ({}: ", "APMRouteHandlerResources", " & { params: { path: { serviceName: string; serviceNodeName: string; }; query: { kuery: string; } & { start: number; end: number; } & { environment: \"ENVIRONMENT_NOT_DEFINED\" | \"ENVIRONMENT_ALL\" | ", "Branded", @@ -5867,7 +5909,15 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; }; }; }) => Promise<", + ">; } & { documentType: ", + "ApmDocumentType", + ".TransactionMetric | ", + "ApmDocumentType", + ".ServiceTransactionMetric | ", + "ApmDocumentType", + ".TransactionEvent; rollupInterval: ", + "RollupInterval", + "; }; }; }) => Promise<", "ServiceNodeMetadataResponse", ">; } & ", "APMRouteCreateOptions", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 8f2354f362229..920537f06ea0b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 23cdee5fff23e..b7415d4dd8001 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 701972b8034cf..8061a2ca975db 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index d39a53dc770c1..558cc4ae30a7a 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-11-22 +date: 2023-11-30 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 028a79e5a56a3..c70d15601728d 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-11-22 +date: 2023-11-30 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 4cfa5ef4851c4..31f503aec9183 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.devdocs.json b/api_docs/cases.devdocs.json index 900da326f6ec2..773a3d4136d1f 100644 --- a/api_docs/cases.devdocs.json +++ b/api_docs/cases.devdocs.json @@ -1314,6 +1314,97 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities", + "type": "Interface", + "tags": [], + "label": "CasesCapabilities", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.CREATE_CASES_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[CREATE_CASES_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.READ_CASES_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[READ_CASES_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.UPDATE_CASES_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[UPDATE_CASES_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.DELETE_CASES_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[DELETE_CASES_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.PUSH_CASES_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[PUSH_CASES_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.CASES_CONNECTORS_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[CASES_CONNECTORS_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesCapabilities.CASES_SETTINGS_CAPABILITY", + "type": "boolean", + "tags": [], + "label": "[CASES_SETTINGS_CAPABILITY]", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "cases", "id": "def-common.CasesPermissions", @@ -1401,6 +1492,17 @@ "path": "x-pack/plugins/cases/common/ui/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesPermissions.settings", + "type": "boolean", + "tags": [], + "label": "settings", + "description": [], + "path": "x-pack/plugins/cases/common/ui/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1457,6 +1559,20 @@ "path": "x-pack/plugins/cases/common/utils/capabilities.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "cases", + "id": "def-common.CasesUiCapabilities.settings", + "type": "Object", + "tags": [], + "label": "settings", + "description": [], + "signature": [ + "readonly string[]" + ], + "path": "x-pack/plugins/cases/common/utils/capabilities.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2192,6 +2308,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "cases", + "id": "def-common.CASES_SETTINGS_CAPABILITY", + "type": "string", + "tags": [], + "label": "CASES_SETTINGS_CAPABILITY", + "description": [], + "signature": [ + "\"cases_settings\"" + ], + "path": "x-pack/plugins/cases/common/constants/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "cases", "id": "def-common.CASES_URL", diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index cbfb20b2bfa00..1c592bbe9df92 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.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 | |-------------------|-----------|------------------------|-----------------| -| 104 | 0 | 84 | 28 | +| 115 | 0 | 95 | 28 | ## Client diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index d43aa52fb7683..dd8c395df407b 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-11-22 +date: 2023-11-30 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 3cb9b226d4ccd..47ef98168188c 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index c58fe300a5944..012af0acb1598 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-11-22 +date: 2023-11-30 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 9ec5343f7afd8..5f636b420311f 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-11-22 +date: 2023-11-30 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 c02454e7a196b..6e3216c615832 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-11-22 +date: 2023-11-30 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 05ee612dd6212..e01df7b3719a7 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-11-22 +date: 2023-11-30 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 aca032a111adc..8f1f19c74f6f7 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-11-22 +date: 2023-11-30 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 4814b33a4f26a..66f0570f9a083 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index c0d2787db1e41..35febfe958f6e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index c632770bdb159..59c19987d604d 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-11-22 +date: 2023-11-30 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 de96d8d3e95e7..5731fb8896f99 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-11-22 +date: 2023-11-30 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 ead8a18229d1e..f28f84acfb43d 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 99149b03fb936..82e38cd78f2f5 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-11-22 +date: 2023-11-30 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 fd74c2a514940..463928ab4e04e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index fe3f4b793d43a..ce39cd72a3b37 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -1656,9 +1656,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1716,9 +1716,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1782,9 +1782,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1834,9 +1834,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1877,9 +1877,9 @@ "SearchSessionStatusDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1929,9 +1929,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -1980,9 +1980,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2040,9 +2040,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2106,9 +2106,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2158,9 +2158,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2216,9 +2216,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2268,9 +2268,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2311,9 +2311,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2347,9 +2347,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2390,9 +2390,9 @@ "SearchSessionDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2426,9 +2426,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2469,9 +2469,9 @@ "SearchSessionStatusDependencies", ", user: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -2513,9 +2513,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 14763fd3fa659..2129f27785292 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-11-22 +date: 2023-11-30 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 9a870d81e2301..1b57b19f9be14 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-11-22 +date: 2023-11-30 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 2ad5cd82b5ba7..e9f0bd6c06dc6 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-11-22 +date: 2023-11-30 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 ab13143d133f7..144c1dd9c3e7b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 704ead5f1acad..f423e382fb519 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-11-22 +date: 2023-11-30 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 cf75ecdea617e..86ad4a73b9d0a 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.devdocs.json b/api_docs/dataset_quality.devdocs.json index 5b46e39efe55a..86ad32dd897cc 100644 --- a/api_docs/dataset_quality.devdocs.json +++ b/api_docs/dataset_quality.devdocs.json @@ -5,7 +5,20 @@ "functions": [], "interfaces": [], "enums": [], - "misc": [], + "misc": [ + { + "parentPluginId": "datasetQuality", + "id": "def-public.datasetQualityAppTitle", + "type": "string", + "tags": [], + "label": "datasetQualityAppTitle", + "description": [], + "path": "x-pack/plugins/dataset_quality/common/translations.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], "objects": [], "setup": { "parentPluginId": "datasetQuality", @@ -31,7 +44,22 @@ "path": "x-pack/plugins/dataset_quality/public/types.ts", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "datasetQuality", + "id": "def-public.DatasetQualityPluginStart.DatasetQuality", + "type": "CompoundType", + "tags": [], + "label": "DatasetQuality", + "description": [], + "signature": [ + "React.ComponentClass<{}, any> | React.FunctionComponent<{}>" + ], + "path": "x-pack/plugins/dataset_quality/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], "lifecycle": "start", "initialIsOpen": true } @@ -64,6 +92,104 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "datasetQuality", + "id": "def-common.APIClientRequestParamsOf", + "type": "Type", + "tags": [], + "label": "APIClientRequestParamsOf", + "description": [], + "signature": [ + "{ \"GET /internal/dataset_quality/data_streams/stats\": { endpoint: \"GET /internal/dataset_quality/data_streams/stats\"; params?: ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "PartialC", + "<{ type: ", + "UnionC", + "<[", + "LiteralC", + "<\"logs\">, ", + "LiteralC", + "<\"metrics\">, ", + "LiteralC", + "<\"traces\">, ", + "LiteralC", + "<\"synthetics\">, ", + "LiteralC", + "<\"profiling\">]>; }>, ", + "PartialC", + "<{ datasetQuery: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "DatasetQualityRouteHandlerResources", + " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<", + "DataStreamsStatResponse", + ">; } & ", + "DatasetQualityRouteCreateOptions", + "; }[TEndpoint] extends { endpoint: any; params?: infer TRouteParamsRT | undefined; handler: ({}: any) => Promise; } & ", + "ServerRouteCreateOptions", + " ? TRouteParamsRT extends ", + { + "pluginId": "@kbn/server-route-repository", + "scope": "common", + "docId": "kibKbnServerRouteRepositoryPluginApi", + "section": "def-common.RouteParamsRT", + "text": "RouteParamsRT" + }, + " ? ClientRequestParamsOfType : {} : never" + ], + "path": "x-pack/plugins/dataset_quality/common/rest/create_call_dataset_quality_api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "datasetQuality", + "id": "def-common.APIReturnType", + "type": "Type", + "tags": [], + "label": "APIReturnType", + "description": [], + "signature": [ + "{ \"GET /internal/dataset_quality/data_streams/stats\": { endpoint: \"GET /internal/dataset_quality/data_streams/stats\"; params?: ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "PartialC", + "<{ type: ", + "UnionC", + "<[", + "LiteralC", + "<\"logs\">, ", + "LiteralC", + "<\"metrics\">, ", + "LiteralC", + "<\"traces\">, ", + "LiteralC", + "<\"synthetics\">, ", + "LiteralC", + "<\"profiling\">]>; }>, ", + "PartialC", + "<{ datasetQuery: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "DatasetQualityRouteHandlerResources", + " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<", + "DataStreamsStatResponse", + ">; } & ", + "DatasetQualityRouteCreateOptions", + "; }[TEndpoint] extends { endpoint: any; params?: any; handler: ({}: any) => Promise; } & ", + "ServerRouteCreateOptions", + " ? TReturnType : never" + ], + "path": "x-pack/plugins/dataset_quality/common/rest/create_call_dataset_quality_api.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "datasetQuality", "id": "def-common.FetchOptions", diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index c4db9ac4e8cc8..bd531645c5a47 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 4 | 0 | 4 | 0 | +| 8 | 0 | 8 | 3 | ## Client @@ -31,6 +31,9 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux ### Start +### Consts, variables and types + + ## Common ### Interfaces diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index d9c3b5811eddf..d9ef62cbd84cc 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -27,9 +27,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | stackAlerts, graph, infra, inputControlVis, securitySolution, savedObjects | - | | | dashboard, dataVisualizer, stackAlerts, expressionPartitionVis | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | +| | triggersActionsUi | - | +| | inspector, data, savedObjects, runtimeFields, indexManagement, dataViewEditor, unifiedSearch, embeddable, visualizations, controls, dashboard, licensing, savedObjectsTagging, dataViewFieldEditor, lens, security, triggersActionsUi, cases, observabilityShared, advancedSettings, exploratoryView, fleet, telemetry, maps, banners, reporting, timelines, cloudSecurityPosture, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, uptime, cloudLinks, console, dataViewManagement, eventAnnotationListing, filesManagement, uiActions, visTypeVislib | - | +| | @kbn/core, visualizations, triggersActionsUi, advancedSettings | - | | | observability, @kbn/securitysolution-data-table, securitySolution | - | | | monitoring | - | -| | inspector, data, savedObjects, runtimeFields, indexManagement, dataViewEditor, unifiedSearch, embeddable, visualizations, controls, dashboard, licensing, savedObjectsTagging, dataViewFieldEditor, lens, security, triggersActionsUi, cases, observabilityShared, advancedSettings, exploratoryView, fleet, telemetry, maps, banners, reporting, timelines, cloudSecurityPosture, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, uptime, cloudLinks, console, dataViewManagement, eventAnnotationListing, filesManagement, uiActions, visTypeVislib | - | | | alerting, discover, securitySolution | - | | | @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-api-server-internal, @kbn/core-saved-objects-import-export-server-internal, @kbn/core-saved-objects-server-internal, fleet, graph, lists, osquery, securitySolution, alerting | - | | | @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-api-server-internal, @kbn/core-saved-objects-import-export-server-internal, @kbn/core-saved-objects-server-internal, fleet, graph, lists, osquery, securitySolution, alerting | - | @@ -122,7 +124,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | canvas | - | | | canvas | - | | | spaces, savedObjectsManagement | - | -| | @kbn/core, visualizations, triggersActionsUi, advancedSettings | - | | | reporting | - | | | @kbn/reporting-export-types-pdf, reporting | - | | | @kbn/core-elasticsearch-server-internal, @kbn/core-plugins-server-internal, observabilityOnboarding, console | - | @@ -135,7 +136,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core-lifecycle-browser-mocks, @kbn/core, @kbn/core-plugins-browser-internal | - | | | @kbn/core | - | | | @kbn/core-plugins-server-internal | - | -| | security, aiops, licenseManagement, ml, profiling, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | +| | security, aiops, licenseManagement, ml, observability, profiling, apm, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | 8.8.0 | | | spaces, security, actions, alerting, aiops, ml, remoteClusters, graph, indexLifecycleManagement, mapsEms, osquery, securitySolution, painlessLab, rollup, searchprofiler, snapshotRestore, transform, upgradeAssistant | 8.8.0 | | | apm, fleet, security, securitySolution | 8.8.0 | | | apm, fleet, security, securitySolution | 8.8.0 | @@ -164,6 +165,8 @@ Safe to remove. | Deprecated API | Plugin Id | | ---------------|------------| | | alerting | +| | alerting | +| | alerting | | | data | | | data | | | data | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 8e6ba2abf80d3..7a03beaa58b70 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -1137,6 +1137,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts#:~:text=alertFactory), [custom_threshold_executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts#:~:text=alertFactory), [executor.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.test.ts#:~:text=alertFactory) | - | +| | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [render_cell_value.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/components/alerts_table/render_cell_value.tsx#:~:text=DeprecatedCellValueElementProps), [render_cell_value.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/components/alerts_table/render_cell_value.tsx#:~:text=DeprecatedCellValueElementProps) | - | @@ -1333,7 +1334,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [app_authorization.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/app_authorization.ts#:~:text=getKibanaFeatures), [privileges.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.ts#:~:text=getKibanaFeatures), [authorization_service.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/authorization_service.tsx#:~:text=getKibanaFeatures), [app_authorization.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/app_authorization.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures), [privileges.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts#:~:text=getKibanaFeatures)+ 15 more | 8.8.0 | | | [authorization_service.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/server/authorization/authorization_service.tsx#:~:text=getElasticsearchFeatures) | 8.8.0 | | | [account_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/account_management_app.tsx#:~:text=toMountPoint), [account_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/account_management/account_management_app.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint), [session_expiration_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/session/session_expiration_toast.tsx#:~:text=toMountPoint) | - | -| | [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [access_agreement_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [logged_out_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [login_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/login/login_page.tsx#:~:text=KibanaThemeProvider), [overwritten_session_page.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx#:~:text=KibanaThemeProvider)+ 20 more | - | +| | [api_keys_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx#:~:text=KibanaThemeProvider), [api_keys_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx#:~:text=KibanaThemeProvider), [api_keys_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx#:~:text=KibanaThemeProvider), [users_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/users/users_management_app.tsx#:~:text=KibanaThemeProvider), [users_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/users/users_management_app.tsx#:~:text=KibanaThemeProvider), [users_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/users/users_management_app.tsx#:~:text=KibanaThemeProvider), [roles_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/roles/roles_management_app.tsx#:~:text=KibanaThemeProvider), [roles_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/roles/roles_management_app.tsx#:~:text=KibanaThemeProvider), [roles_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/roles/roles_management_app.tsx#:~:text=KibanaThemeProvider), [role_mappings_management_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx#:~:text=KibanaThemeProvider)+ 20 more | - | | | [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode) | 8.8.0 | | | [plugin.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/public/plugin.tsx#:~:text=license%24) | 8.8.0 | | | [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode), [license_service.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security/common/licensing/license_service.test.ts#:~:text=mode) | 8.8.0 | @@ -1372,10 +1373,10 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | | | [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) | - | | | [timelines.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts#:~:text=convertToMultiNamespaceTypeVersion), [notes.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts#:~:text=convertToMultiNamespaceTypeVersion), [pinned_events.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts#:~:text=convertToMultiNamespaceTypeVersion), [legacy_saved_object_mappings.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | -| | [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [trusted_app_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [trusted_app_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [receiver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [receiver.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID)+ 37 more | - | +| | [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [trusted_app_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [trusted_app_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID)+ 35 more | - | | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME) | - | | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION) | - | -| | [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [event_filter_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/event_filter_validator.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [event_filter_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/event_filter_validator.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [security_lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID)+ 35 more | - | +| | [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [event_filter_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/event_filter_validator.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [event_filter_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/event_filter_validator.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_ID)+ 33 more | - | | | [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_NAME) | - | | | [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION), [create_event_filters.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts#:~:text=ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION) | - | | | [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [host_isolation_exceptions_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/host_isolation_exceptions_validator.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [host_isolation_exceptions_validator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/host_isolation_exceptions_validator.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/constants.ts#:~:text=ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID)+ 19 more | - | @@ -1488,6 +1489,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [tooltip_content.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx#:~:text=MaintenanceWindow), [tooltip_content.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/tooltip_content.tsx#:~:text=MaintenanceWindow), [cell.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx#:~:text=MaintenanceWindow), [cell.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx#:~:text=MaintenanceWindow), [cell.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx#:~:text=MaintenanceWindow), [cell.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/maintenance_windows/cell.tsx#:~:text=MaintenanceWindow), [bulk_get_maintenance_windows.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts#:~:text=MaintenanceWindow), [bulk_get_maintenance_windows.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts#:~:text=MaintenanceWindow), [bulk_get_maintenance_windows.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts#:~:text=MaintenanceWindow), [bulk_get_maintenance_windows.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/bulk_get_maintenance_windows.ts#:~:text=MaintenanceWindow)+ 11 more | - | | | [use_bulk_edit_response.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_response.tsx#:~:text=toMountPoint), [use_bulk_edit_response.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_response.tsx#:~:text=toMountPoint), [use_bulk_edit_response.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_response.tsx#:~:text=toMountPoint), [use_bulk_operation_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_operation_toast.tsx#:~:text=toMountPoint), [use_bulk_operation_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_operation_toast.tsx#:~:text=toMountPoint), [use_bulk_operation_toast.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_operation_toast.tsx#:~:text=toMountPoint), [rule_add.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx#:~:text=toMountPoint), [rule_add.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx#:~:text=toMountPoint), [rule_edit.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx#:~:text=toMountPoint), [rule_edit.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx#:~:text=toMountPoint)+ 6 more | - | | | [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/app.tsx#:~:text=KibanaThemeProvider), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/app.tsx#:~:text=KibanaThemeProvider), [app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/app.tsx#:~:text=KibanaThemeProvider), [connectors_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx#:~:text=KibanaThemeProvider), [connectors_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx#:~:text=KibanaThemeProvider), [connectors_app.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx#:~:text=KibanaThemeProvider), [test_utils.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/test_utils.tsx#:~:text=KibanaThemeProvider), [test_utils.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/test_utils.tsx#:~:text=KibanaThemeProvider), [test_utils.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/test_utils.tsx#:~:text=KibanaThemeProvider) | - | | | [rule_reducer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts#:~:text=SavedObjectAttribute), [rule_reducer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts#:~:text=SavedObjectAttribute), [rule_reducer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts#:~:text=SavedObjectAttribute), [rule_reducer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts#:~:text=SavedObjectAttribute) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index ee4ea593d89a1..5733d3a301935 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -124,6 +124,14 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ +## @elastic/obs-ux-management-team + +| Plugin | Deprecated API | Reference location(s) | Remove By | +| --------|-------|-----------|-----------| +| observability | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/plugin.ts#:~:text=license%24) | 8.8.0 | + + + ## @elastic/platform-deployment-management | Plugin | Deprecated API | Reference location(s) | Remove By | diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 4b9e45fb4540c..606e30fcb7bbb 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 307409fec37cb..5b01be8304df7 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -993,6 +993,21 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "discover", + "id": "def-public.FlyoutCustomization.size", + "type": "CompoundType", + "tags": [], + "label": "size", + "description": [], + "signature": [ + "Property", + ".Width | undefined" + ], + "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "discover", "id": "def-public.FlyoutCustomization.title", diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 9d64958d96828..2b6a9b1e2eb17 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 133 | 0 | 90 | 20 | +| 134 | 0 | 91 | 20 | ## Client diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index a8bcb007817af..6b33b2e6c94ca 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-11-22 +date: 2023-11-30 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 7c2025feb2811..15e4ab2986274 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 5792e76cf66fd..244f976ce9ad7 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 767db689ceca3..8b1a1e4e3568d 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-11-22 +date: 2023-11-30 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 ca652ba0e4eaa..056c6d15173b9 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-11-22 +date: 2023-11-30 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 5c2c281098155..4e58bcd454f3c 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-11-22 +date: 2023-11-30 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 01a4636427c97..a77a21f6614b9 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-11-22 +date: 2023-11-30 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 7611986910be3..3dfac3f762d0e 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-11-22 +date: 2023-11-30 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 fe565a8485ddc..5313045188d0d 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 3f75781c9aadb..1f75f1401d217 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index c267d5876cced..cfb0ffc9e1dc4 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.devdocs.json b/api_docs/exploratory_view.devdocs.json index 299ed7b02041d..0e55c27726762 100644 --- a/api_docs/exploratory_view.devdocs.json +++ b/api_docs/exploratory_view.devdocs.json @@ -1532,7 +1532,7 @@ "label": "defaultSeriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/types.ts", "deprecated": false, diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 0089a43642d84..6963137fb77a9 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index b5f1461014552..2f5dad63bc0fd 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-11-22 +date: 2023-11-30 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 b9430e72111aa..fd706ce1e71a1 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-11-22 +date: 2023-11-30 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 1e41e0d52d243..6bab5564f1e4c 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-11-22 +date: 2023-11-30 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 bab05c0018345..10c07dd4f8d84 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-11-22 +date: 2023-11-30 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 143df1b214d47..fc73b804ff2b6 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-11-22 +date: 2023-11-30 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 c658d8ef54998..a51cccb44559f 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-11-22 +date: 2023-11-30 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 099c1b68c6d6a..d92e882c05e62 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-11-22 +date: 2023-11-30 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 f516ba6588c31..ce09249de1fa3 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-11-22 +date: 2023-11-30 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 85d92c843f4f6..0532fa0793b75 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-11-22 +date: 2023-11-30 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 7dd1bc25fb7d7..4ff918016c804 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.devdocs.json b/api_docs/expression_shape.devdocs.json index 8e3a2a1d04c5e..3c6d97d7ce0f4 100644 --- a/api_docs/expression_shape.devdocs.json +++ b/api_docs/expression_shape.devdocs.json @@ -635,7 +635,7 @@ "label": "strokeLinecap", "description": [], "signature": [ - "\"butt\" | \"round\" | \"square\" | \"inherit\" | undefined" + "\"inherit\" | \"butt\" | \"round\" | \"square\" | undefined" ], "path": "src/plugins/expression_shape/public/components/reusable/types.tsx", "deprecated": false, @@ -677,7 +677,7 @@ "label": "strokeLinejoin", "description": [], "signature": [ - "\"round\" | \"inherit\" | \"miter\" | \"bevel\" | undefined" + "\"inherit\" | \"round\" | \"miter\" | \"bevel\" | undefined" ], "path": "src/plugins/expression_shape/public/components/reusable/types.tsx", "deprecated": false, diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 0385bb47c320d..a71cc14f67571 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-11-22 +date: 2023-11-30 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 edb2a90059149..d4f2e2a44a391 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index 557d8749fb6de..8f9e4a2d6e27c 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -576,7 +576,7 @@ "label": "seriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\"" + "\"area\" | \"line\" | \"bar\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, @@ -3340,7 +3340,7 @@ "label": "SeriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\"" + "\"area\" | \"line\" | \"bar\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index a36a497652b19..dd7908b619ba2 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.devdocs.json b/api_docs/expressions.devdocs.json index 6c9f54399a958..147e417c0ce6d 100644 --- a/api_docs/expressions.devdocs.json +++ b/api_docs/expressions.devdocs.json @@ -11700,7 +11700,7 @@ "label": "padding", "description": [], "signature": [ - "\"m\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined" + "\"m\" | \"s\" | \"l\" | \"xs\" | \"xl\" | undefined" ], "path": "src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx", "deprecated": false, @@ -37964,7 +37964,7 @@ "\nMode of the expression render environment.\nThis value can be set from a consumer embedding an expression renderer and is accessible\nfrom within the active render function as part of the handlers.\nThe following modes are supported:\n* view (default): The chart is rendered in a container with the main purpose of viewing the chart (e.g. in a container like dashboard or canvas)\n* preview: The chart is rendered in very restricted space (below 100px width and height) and should only show a rough outline\n* edit: The chart is rendered within an editor and configuration elements within the chart should be displayed" ], "signature": [ - "\"edit\" | \"preview\" | \"view\"" + "\"edit\" | \"view\" | \"preview\"" ], "path": "src/plugins/expressions/common/expression_renderers/types.ts", "deprecated": false, diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 4d0ee3be340b9..ea4b579872f32 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.devdocs.json b/api_docs/features.devdocs.json index 3f329d7f351b6..27e89ae753ed1 100644 --- a/api_docs/features.devdocs.json +++ b/api_docs/features.devdocs.json @@ -56,7 +56,7 @@ "label": "config", "description": [], "signature": [ - "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", + "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", { "pluginId": "features", "scope": "common", @@ -64,7 +64,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -202,7 +202,7 @@ "label": "privileges", "description": [], "signature": [ - "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" + "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -255,7 +255,7 @@ "label": "reserved", "description": [], "signature": [ - "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" + "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -441,7 +441,7 @@ "\nIf your feature requires access to specific owners of cases (aka plugins that have created cases), then specify your access needs here. The values here should\nbe unique identifiers for the owners of cases you want access to." ], "signature": [ - "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; } | undefined" + "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; } | undefined" ], "path": "x-pack/plugins/features/common/feature_kibana_privileges.ts", "deprecated": false, @@ -1264,7 +1264,7 @@ "label": "config", "description": [], "signature": [ - "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", + "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", { "pluginId": "features", "scope": "common", @@ -1272,7 +1272,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -1410,7 +1410,7 @@ "label": "privileges", "description": [], "signature": [ - "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" + "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -1463,7 +1463,7 @@ "label": "reserved", "description": [], "signature": [ - "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" + "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -1828,7 +1828,7 @@ "\nIf your feature requires access to specific owners of cases (aka plugins that have created cases), then specify your access needs here. The values here should\nbe unique identifiers for the owners of cases you want access to." ], "signature": [ - "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; } | undefined" + "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; } | undefined" ], "path": "x-pack/plugins/features/common/feature_kibana_privileges.ts", "deprecated": false, @@ -2961,7 +2961,7 @@ "label": "config", "description": [], "signature": [ - "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", + "Readonly<{ id: string; name: string; description?: string | undefined; category: Readonly<{ id: string; label: string; ariaLabel?: string | undefined; order?: number | undefined; euiIconType?: string | undefined; }>; order?: number | undefined; excludeFromBasePrivileges?: boolean | undefined; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; app: readonly string[]; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; alerting?: readonly string[] | undefined; cases?: readonly string[] | undefined; privileges: Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null; subFeatures?: readonly Readonly<{ name: string; requireAllSpaces?: boolean | undefined; privilegesTooltip?: string | undefined; privilegeGroups: readonly Readonly<{ groupType: ", { "pluginId": "features", "scope": "common", @@ -2969,7 +2969,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>[] | undefined; privilegesTooltip?: string | undefined; reserved?: Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined; }>" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -3107,7 +3107,7 @@ "label": "privileges", "description": [], "signature": [ - "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" + "Readonly<{ all: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; read: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }> | null" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -3160,7 +3160,7 @@ "label": "reserved", "description": [], "signature": [ - "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" + "Readonly<{ description: string; privileges: readonly Readonly<{ id: string; privilege: Readonly<{ excludeFromBasePrivileges?: boolean | undefined; requireAllSpaces?: boolean | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; catalogue?: readonly string[] | undefined; api?: readonly string[] | undefined; app?: readonly string[] | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; ui: readonly string[]; }>; }>[]; }> | undefined" ], "path": "x-pack/plugins/features/common/kibana_feature.ts", "deprecated": false, @@ -3233,7 +3233,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }>" ], "path": "x-pack/plugins/features/common/sub_feature.ts", "deprecated": false, @@ -3270,7 +3270,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]" ], "path": "x-pack/plugins/features/common/sub_feature.ts", "deprecated": false, @@ -3314,7 +3314,7 @@ "section": "def-common.SubFeaturePrivilegeGroupType", "text": "SubFeaturePrivilegeGroupType" }, - "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }" + "; privileges: readonly Readonly<{ id: string; name: string; includeIn: \"none\" | \"read\" | \"all\"; minimumLicense?: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\" | undefined; alerting?: Readonly<{ rule?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; alert?: Readonly<{ all?: readonly string[] | undefined; read?: readonly string[] | undefined; }> | undefined; }> | undefined; cases?: Readonly<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; }> | undefined; disabled?: boolean | undefined; management?: Readonly<{ [x: string]: readonly string[]; }> | undefined; ui: readonly string[]; catalogue?: readonly string[] | undefined; app?: readonly string[] | undefined; requireAllSpaces?: boolean | undefined; api?: readonly string[] | undefined; savedObject: Readonly<{ all: readonly string[]; read: readonly string[]; }>; }>[]; }>[]; description?: string | undefined; }" ], "path": "x-pack/plugins/features/common/sub_feature.ts", "deprecated": false, @@ -3658,7 +3658,7 @@ "\nIf your feature requires access to specific owners of cases (aka plugins that have created cases), then specify your access needs here. The values here should\nbe unique identifiers for the owners of cases you want access to." ], "signature": [ - "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; } | undefined" + "{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; } | undefined" ], "path": "x-pack/plugins/features/common/feature_kibana_privileges.ts", "deprecated": false, diff --git a/api_docs/features.mdx b/api_docs/features.mdx index ad02d5e4bf314..68e30f9962b27 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-11-22 +date: 2023-11-30 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 2ee0e101b1bb6..a73bd48e1094f 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-11-22 +date: 2023-11-30 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 e53ede098ba87..397cf2b9a6074 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 2c686cc62f2b2..70db8231e2c9e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 579409f0f470d..130f51934ddb6 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-11-22 +date: 2023-11-30 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 6aedc7943b644..a57d03da5d59f 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -7146,9 +7146,9 @@ }, ", options?: { spaceId?: string | undefined; id?: string | undefined; user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -7303,9 +7303,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -7569,9 +7569,9 @@ "NewPackagePolicyWithId", "[], options?: { user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -7685,9 +7685,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -7779,9 +7779,9 @@ }, "[], options?: { user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -7893,9 +7893,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8382,9 +8382,9 @@ }, ", options?: { user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8500,9 +8500,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8586,9 +8586,9 @@ }, ", ids: string[], options?: { user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8699,9 +8699,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8814,9 +8814,9 @@ }, ", ids: string[], options?: { user?: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -8919,9 +8919,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -20639,6 +20639,21 @@ "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.Installation.latest_install_failed_attempts", + "type": "Array", + "tags": [], + "label": "latest_install_failed_attempts", + "description": [], + "signature": [ + "InstallFailedAttempt", + "[] | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -23426,7 +23441,7 @@ "label": "[RegistryVarsEntryKeys.type]", "description": [], "signature": [ - "\"string\" | \"text\" | \"integer\" | \"select\" | \"bool\" | \"password\" | \"yaml\" | \"textarea\"" + "\"string\" | \"text\" | \"integer\" | \"select\" | \"textarea\" | \"bool\" | \"password\" | \"yaml\"" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 7123c481c7233..6a3f7491c81a9 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-11-22 +date: 2023-11-30 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 | |-------------------|-----------|------------------------|-----------------| -| 1212 | 3 | 1094 | 46 | +| 1213 | 3 | 1095 | 47 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 24023aadf85f7..fff05062156e8 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-11-22 +date: 2023-11-30 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 b8f50ae5cb6ac..5698163c47c81 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-11-22 +date: 2023-11-30 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 3c492bc772d10..3d8eeeb6e97f2 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-11-22 +date: 2023-11-30 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 9aa80bec82b35..e3c023ca167cf 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-11-22 +date: 2023-11-30 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 a85762ab6be8f..d462edb008e00 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-11-22 +date: 2023-11-30 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 4d33cbdbc04f3..bc91cf4e0e432 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.devdocs.json b/api_docs/infra.devdocs.json index 6329b376e20cc..7c1db61528e47 100644 --- a/api_docs/infra.devdocs.json +++ b/api_docs/infra.devdocs.json @@ -358,7 +358,7 @@ "label": "featureFlags", "description": [], "signature": [ - "{ customThresholdAlertsEnabled: boolean; logsUIEnabled: boolean; metricsExplorerEnabled: boolean; osqueryEnabled: boolean; inventoryThresholdAlertRuleEnabled: boolean; metricThresholdAlertRuleEnabled: boolean; logThresholdAlertRuleEnabled: boolean; alertsAndRulesDropdownEnabled: boolean; }" + "{ customThresholdAlertsEnabled: boolean; logsUIEnabled: boolean; metricsExplorerEnabled: boolean; osqueryEnabled: boolean; inventoryThresholdAlertRuleEnabled: boolean; metricThresholdAlertRuleEnabled: boolean; logThresholdAlertRuleEnabled: boolean; alertsAndRulesDropdownEnabled: boolean; profilingEnabled: boolean; }" ], "path": "x-pack/plugins/infra/common/plugin_config_types.ts", "deprecated": false, diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 4ed281a08f3db..e587731395ffa 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-11-22 +date: 2023-11-30 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 d3b86b7b90230..268f238a50baa 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-11-22 +date: 2023-11-30 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 30ff7542a63c8..669976b59f5ab 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-11-22 +date: 2023-11-30 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 82c5552f60bd3..7f90ed50cdae4 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-11-22 +date: 2023-11-30 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 94c9325349de7..daa559388ed16 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.devdocs.json b/api_docs/kbn_aiops_utils.devdocs.json index 4ab34fcaf1db4..b733fd1318ca7 100644 --- a/api_docs/kbn_aiops_utils.devdocs.json +++ b/api_docs/kbn_aiops_utils.devdocs.json @@ -336,12 +336,10 @@ "parentPluginId": "@kbn/aiops-utils", "id": "def-common.WindowParameters.baselineMin", "type": "number", - "tags": [ - "type" - ], + "tags": [], "label": "baselineMin", "description": [ - "\nBaseline minimum value" + "Baseline minimum value" ], "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, @@ -351,12 +349,10 @@ "parentPluginId": "@kbn/aiops-utils", "id": "def-common.WindowParameters.baselineMax", "type": "number", - "tags": [ - "type" - ], + "tags": [], "label": "baselineMax", "description": [ - "\nBaseline maximum value" + "Baseline maximum value" ], "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, @@ -366,12 +362,10 @@ "parentPluginId": "@kbn/aiops-utils", "id": "def-common.WindowParameters.deviationMin", "type": "number", - "tags": [ - "type" - ], + "tags": [], "label": "deviationMin", "description": [ - "\nDeviation minimum value" + "Deviation minimum value" ], "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, @@ -381,12 +375,10 @@ "parentPluginId": "@kbn/aiops-utils", "id": "def-common.WindowParameters.deviationMax", "type": "number", - "tags": [ - "type" - ], + "tags": [], "label": "deviationMax", "description": [ - "\nDeviation maximum value" + "Deviation maximum value" ], "path": "x-pack/packages/ml/aiops_utils/window_parameters.ts", "deprecated": false, diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index a31a03bd9f3dc..b76cb84554864 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index f95c5d20df809..05ca9737bb8fa 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index c7f7f0f964b7c..85da58fb6dfae 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.devdocs.json b/api_docs/kbn_alerts_as_data_utils.devdocs.json index 8f3c9ec250b05..9914e93730bb7 100644 --- a/api_docs/kbn_alerts_as_data_utils.devdocs.json +++ b/api_docs/kbn_alerts_as_data_utils.devdocs.json @@ -196,7 +196,7 @@ "label": "AADAlert", "description": [], "signature": [ - "({ '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'agent.name'?: string | undefined; 'error.grouping_key'?: string | undefined; 'error.grouping_name'?: string | undefined; 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; labels?: unknown; 'processor.event'?: string | undefined; 'service.environment'?: string | undefined; 'service.language.name'?: string | undefined; 'service.name'?: string | undefined; 'transaction.name'?: string | undefined; 'transaction.type'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'slo.id'?: string | undefined; 'slo.instanceId'?: string | undefined; 'slo.revision'?: string | number | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; 'error.message'?: string | undefined; 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.x509.issuer.common_name'?: string | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.subject.common_name'?: string | undefined; 'url.full'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({ '@timestamp': string | number; 'kibana.alert.ancestors': { depth: string | number; id: string; index: string; type: string; }[]; 'kibana.alert.depth': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.original_event.action': string; 'kibana.alert.original_event.category': string[]; 'kibana.alert.original_event.created': string | number; 'kibana.alert.original_event.dataset': string; 'kibana.alert.original_event.id': string; 'kibana.alert.original_event.ingested': string | number; 'kibana.alert.original_event.kind': string; 'kibana.alert.original_event.module': string; 'kibana.alert.original_event.original': string; 'kibana.alert.original_event.outcome': string; 'kibana.alert.original_event.provider': string; 'kibana.alert.original_event.sequence': string | number; 'kibana.alert.original_event.type': string[]; 'kibana.alert.original_time': string | number; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.false_positives': string[]; 'kibana.alert.rule.max_signals': (string | number)[]; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.threat.framework': string; 'kibana.alert.rule.threat.tactic.id': string; 'kibana.alert.rule.threat.tactic.name': string; 'kibana.alert.rule.threat.tactic.reference': string; 'kibana.alert.rule.threat.technique.id': string; 'kibana.alert.rule.threat.technique.name': string; 'kibana.alert.rule.threat.technique.reference': string; 'kibana.alert.rule.threat.technique.subtechnique.id': string; 'kibana.alert.rule.threat.technique.subtechnique.name': string; 'kibana.alert.rule.threat.technique.subtechnique.reference': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'ecs.version'?: string | undefined; 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.ancestors.rule'?: string | undefined; 'kibana.alert.building_block_type'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.group.id'?: string | undefined; 'kibana.alert.group.index'?: number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.new_terms'?: string[] | undefined; 'kibana.alert.original_event.agent_id_status'?: string | undefined; 'kibana.alert.original_event.code'?: string | undefined; 'kibana.alert.original_event.duration'?: string | undefined; 'kibana.alert.original_event.end'?: string | number | undefined; 'kibana.alert.original_event.hash'?: string | undefined; 'kibana.alert.original_event.reason'?: string | undefined; 'kibana.alert.original_event.reference'?: string | undefined; 'kibana.alert.original_event.risk_score'?: number | undefined; 'kibana.alert.original_event.risk_score_norm'?: number | undefined; 'kibana.alert.original_event.severity'?: string | number | undefined; 'kibana.alert.original_event.start'?: string | number | undefined; 'kibana.alert.original_event.timezone'?: string | undefined; 'kibana.alert.original_event.url'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.building_block_type'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | undefined; 'kibana.alert.rule.created_by'?: string | undefined; 'kibana.alert.rule.description'?: string | undefined; 'kibana.alert.rule.enabled'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.from'?: string | undefined; 'kibana.alert.rule.immutable'?: string[] | undefined; 'kibana.alert.rule.interval'?: string | undefined; 'kibana.alert.rule.license'?: string | undefined; 'kibana.alert.rule.note'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; '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.timeline_id'?: string[] | undefined; 'kibana.alert.rule.timeline_title'?: string[] | undefined; 'kibana.alert.rule.timestamp_override'?: string | undefined; 'kibana.alert.rule.to'?: string | undefined; 'kibana.alert.rule.type'?: string | undefined; 'kibana.alert.rule.updated_at'?: string | number | undefined; 'kibana.alert.rule.updated_by'?: string | undefined; 'kibana.alert.rule.version'?: string | undefined; 'kibana.alert.severity'?: string | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.suppression.docs_count'?: string | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: string[] | undefined; 'kibana.alert.suppression.terms.value'?: string[] | undefined; 'kibana.alert.system_status'?: string | undefined; 'kibana.alert.threshold_result.cardinality'?: unknown; 'kibana.alert.threshold_result.count'?: string | number | undefined; 'kibana.alert.threshold_result.from'?: string | number | undefined; 'kibana.alert.threshold_result.terms'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({ 'kibana.alert.job_id': string; } & { 'kibana.alert.anomaly_score'?: number | undefined; 'kibana.alert.anomaly_timestamp'?: string | number | undefined; 'kibana.alert.is_interim'?: boolean | undefined; 'kibana.alert.top_influencers'?: { influencer_field_name?: string | undefined; influencer_field_value?: string | undefined; influencer_score?: number | undefined; initial_influencer_score?: number | undefined; is_interim?: boolean | undefined; job_id?: string | undefined; timestamp?: string | number | undefined; }[] | undefined; 'kibana.alert.top_records'?: { actual?: number | undefined; by_field_name?: string | undefined; by_field_value?: string | undefined; detector_index?: number | undefined; field_name?: string | undefined; function?: string | undefined; initial_record_score?: number | undefined; is_interim?: boolean | undefined; job_id?: string | undefined; over_field_name?: string | undefined; over_field_value?: string | undefined; partition_field_name?: string | undefined; partition_field_value?: string | undefined; record_score?: number | undefined; timestamp?: string | number | undefined; typical?: number | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; })" + "({ '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }) | ({} & { 'agent.name'?: string | undefined; 'error.grouping_key'?: string | undefined; 'error.grouping_name'?: string | undefined; 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; labels?: unknown; 'processor.event'?: string | undefined; 'service.environment'?: string | undefined; 'service.language.name'?: string | undefined; 'service.name'?: string | undefined; 'transaction.name'?: string | undefined; 'transaction.type'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'slo.id'?: string | undefined; 'slo.instanceId'?: string | undefined; 'slo.revision'?: string | number | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({} & { 'agent.name'?: string | undefined; 'anomaly.bucket_span.minutes'?: string | undefined; 'anomaly.start'?: string | number | undefined; 'error.message'?: string | undefined; 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'monitor.id'?: string | undefined; 'monitor.name'?: string | undefined; 'monitor.type'?: string | undefined; 'observer.geo.name'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.x509.issuer.common_name'?: string | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.subject.common_name'?: string | undefined; 'url.full'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({ '@timestamp': string | number; 'kibana.alert.ancestors': { depth: string | number; id: string; index: string; type: string; }[]; 'kibana.alert.depth': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.original_event.action': string; 'kibana.alert.original_event.category': string[]; 'kibana.alert.original_event.created': string | number; 'kibana.alert.original_event.dataset': string; 'kibana.alert.original_event.id': string; 'kibana.alert.original_event.ingested': string | number; 'kibana.alert.original_event.kind': string; 'kibana.alert.original_event.module': string; 'kibana.alert.original_event.original': string; 'kibana.alert.original_event.outcome': string; 'kibana.alert.original_event.provider': string; 'kibana.alert.original_event.sequence': string | number; 'kibana.alert.original_event.type': string[]; 'kibana.alert.original_time': string | number; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.false_positives': string[]; 'kibana.alert.rule.max_signals': (string | number)[]; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.threat.framework': string; 'kibana.alert.rule.threat.tactic.id': string; 'kibana.alert.rule.threat.tactic.name': string; 'kibana.alert.rule.threat.tactic.reference': string; 'kibana.alert.rule.threat.technique.id': string; 'kibana.alert.rule.threat.technique.name': string; 'kibana.alert.rule.threat.technique.reference': string; 'kibana.alert.rule.threat.technique.subtechnique.id': string; 'kibana.alert.rule.threat.technique.subtechnique.name': string; 'kibana.alert.rule.threat.technique.subtechnique.reference': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'ecs.version'?: string | undefined; 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.ancestors.rule'?: string | undefined; 'kibana.alert.building_block_type'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.group.id'?: string | undefined; 'kibana.alert.group.index'?: number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.new_terms'?: string[] | undefined; 'kibana.alert.original_event.agent_id_status'?: string | undefined; 'kibana.alert.original_event.code'?: string | undefined; 'kibana.alert.original_event.duration'?: string | undefined; 'kibana.alert.original_event.end'?: string | number | undefined; 'kibana.alert.original_event.hash'?: string | undefined; 'kibana.alert.original_event.reason'?: string | undefined; 'kibana.alert.original_event.reference'?: string | undefined; 'kibana.alert.original_event.risk_score'?: number | undefined; 'kibana.alert.original_event.risk_score_norm'?: number | undefined; 'kibana.alert.original_event.severity'?: string | number | undefined; 'kibana.alert.original_event.start'?: string | number | undefined; 'kibana.alert.original_event.timezone'?: string | undefined; 'kibana.alert.original_event.url'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.building_block_type'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | undefined; 'kibana.alert.rule.created_by'?: string | undefined; 'kibana.alert.rule.description'?: string | undefined; 'kibana.alert.rule.enabled'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.from'?: string | undefined; 'kibana.alert.rule.immutable'?: string[] | undefined; 'kibana.alert.rule.interval'?: string | undefined; 'kibana.alert.rule.license'?: string | undefined; 'kibana.alert.rule.note'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; '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.timeline_id'?: string[] | undefined; 'kibana.alert.rule.timeline_title'?: string[] | undefined; 'kibana.alert.rule.timestamp_override'?: string | undefined; 'kibana.alert.rule.to'?: string | undefined; 'kibana.alert.rule.type'?: string | undefined; 'kibana.alert.rule.updated_at'?: string | number | undefined; 'kibana.alert.rule.updated_by'?: string | undefined; 'kibana.alert.rule.version'?: string | undefined; 'kibana.alert.severity'?: string | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.suppression.docs_count'?: string | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: string[] | undefined; 'kibana.alert.suppression.terms.value'?: string[] | undefined; 'kibana.alert.system_status'?: string | undefined; 'kibana.alert.threshold_result.cardinality'?: unknown; 'kibana.alert.threshold_result.count'?: string | number | undefined; 'kibana.alert.threshold_result.from'?: string | number | undefined; 'kibana.alert.threshold_result.terms'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }) | ({ 'kibana.alert.job_id': string; } & { 'kibana.alert.anomaly_score'?: number | undefined; 'kibana.alert.anomaly_timestamp'?: string | number | undefined; 'kibana.alert.is_interim'?: boolean | undefined; 'kibana.alert.top_influencers'?: { influencer_field_name?: string | undefined; influencer_field_value?: string | undefined; influencer_score?: number | undefined; initial_influencer_score?: number | undefined; is_interim?: boolean | undefined; job_id?: string | undefined; timestamp?: string | number | undefined; }[] | undefined; 'kibana.alert.top_records'?: { actual?: number | undefined; by_field_name?: string | undefined; by_field_value?: string | undefined; detector_index?: number | undefined; field_name?: string | undefined; function?: string | undefined; initial_record_score?: number | undefined; is_interim?: boolean | undefined; job_id?: string | undefined; over_field_name?: string | undefined; over_field_value?: string | undefined; partition_field_name?: string | undefined; partition_field_value?: string | undefined; record_score?: number | undefined; timestamp?: string | number | undefined; typical?: number | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; })" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/index.ts", "deprecated": false, @@ -241,7 +241,15 @@ "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.maintenance_window_ids\": { readonly type: \"keyword\"; 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.revision\": { readonly type: \"long\"; 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.url\": { readonly type: \"keyword\"; readonly array: false; readonly index: false; readonly required: false; readonly ignore_above: 2048; }; 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.alert.workflow_tags\": { readonly type: \"keyword\"; readonly array: true; 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 \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"@timestamp\": { readonly type: \"date\"; readonly required: true; readonly array: false; }; readonly \"kibana.version\": { readonly type: \"version\"; readonly array: false; readonly required: false; }; }" + "{ 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.maintenance_window_ids\": { readonly type: \"keyword\"; 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 multi_fields: ", + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.MultiField", + "text": "MultiField" + }, + "[]; }; 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.revision\": { readonly type: \"long\"; 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.url\": { readonly type: \"keyword\"; readonly array: false; readonly index: false; readonly required: false; readonly ignore_above: 2048; }; 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.alert.workflow_tags\": { readonly type: \"keyword\"; readonly array: true; 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 \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; 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, @@ -337,7 +345,7 @@ "label": "ObservabilityLogsAlert", "description": [], "signature": [ - "{} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" + "{} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts", "deprecated": false, @@ -352,7 +360,7 @@ "label": "ObservabilityMetricsAlert", "description": [], "signature": [ - "{} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" + "{} & { 'kibana.alert.context'?: unknown; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | number | undefined; 'kibana.alert.evaluation.values'?: (string | number)[] | undefined; 'kibana.alert.group'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts", "deprecated": false, @@ -397,7 +405,7 @@ "label": "SecurityAlert", "description": [], "signature": [ - "{ '@timestamp': string | number; 'kibana.alert.ancestors': { depth: string | number; id: string; index: string; type: string; }[]; 'kibana.alert.depth': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.original_event.action': string; 'kibana.alert.original_event.category': string[]; 'kibana.alert.original_event.created': string | number; 'kibana.alert.original_event.dataset': string; 'kibana.alert.original_event.id': string; 'kibana.alert.original_event.ingested': string | number; 'kibana.alert.original_event.kind': string; 'kibana.alert.original_event.module': string; 'kibana.alert.original_event.original': string; 'kibana.alert.original_event.outcome': string; 'kibana.alert.original_event.provider': string; 'kibana.alert.original_event.sequence': string | number; 'kibana.alert.original_event.type': string[]; 'kibana.alert.original_time': string | number; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.false_positives': string[]; 'kibana.alert.rule.max_signals': (string | number)[]; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.threat.framework': string; 'kibana.alert.rule.threat.tactic.id': string; 'kibana.alert.rule.threat.tactic.name': string; 'kibana.alert.rule.threat.tactic.reference': string; 'kibana.alert.rule.threat.technique.id': string; 'kibana.alert.rule.threat.technique.name': string; 'kibana.alert.rule.threat.technique.reference': string; 'kibana.alert.rule.threat.technique.subtechnique.id': string; 'kibana.alert.rule.threat.technique.subtechnique.name': string; 'kibana.alert.rule.threat.technique.subtechnique.reference': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'ecs.version'?: string | undefined; 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.ancestors.rule'?: string | undefined; 'kibana.alert.building_block_type'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.group.id'?: string | undefined; 'kibana.alert.group.index'?: number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.new_terms'?: string[] | undefined; 'kibana.alert.original_event.agent_id_status'?: string | undefined; 'kibana.alert.original_event.code'?: string | undefined; 'kibana.alert.original_event.duration'?: string | undefined; 'kibana.alert.original_event.end'?: string | number | undefined; 'kibana.alert.original_event.hash'?: string | undefined; 'kibana.alert.original_event.reason'?: string | undefined; 'kibana.alert.original_event.reference'?: string | undefined; 'kibana.alert.original_event.risk_score'?: number | undefined; 'kibana.alert.original_event.risk_score_norm'?: number | undefined; 'kibana.alert.original_event.severity'?: string | number | undefined; 'kibana.alert.original_event.start'?: string | number | undefined; 'kibana.alert.original_event.timezone'?: string | undefined; 'kibana.alert.original_event.url'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.building_block_type'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | undefined; 'kibana.alert.rule.created_by'?: string | undefined; 'kibana.alert.rule.description'?: string | undefined; 'kibana.alert.rule.enabled'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.from'?: string | undefined; 'kibana.alert.rule.immutable'?: string[] | undefined; 'kibana.alert.rule.interval'?: string | undefined; 'kibana.alert.rule.license'?: string | undefined; 'kibana.alert.rule.note'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; '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.timeline_id'?: string[] | undefined; 'kibana.alert.rule.timeline_title'?: string[] | undefined; 'kibana.alert.rule.timestamp_override'?: string | undefined; 'kibana.alert.rule.to'?: string | undefined; 'kibana.alert.rule.type'?: string | undefined; 'kibana.alert.rule.updated_at'?: string | number | undefined; 'kibana.alert.rule.updated_by'?: string | undefined; 'kibana.alert.rule.version'?: string | undefined; 'kibana.alert.severity'?: string | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.suppression.docs_count'?: string | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: string[] | undefined; 'kibana.alert.suppression.terms.value'?: string[] | undefined; 'kibana.alert.system_status'?: string | undefined; 'kibana.alert.threshold_result.cardinality'?: unknown; 'kibana.alert.threshold_result.count'?: string | number | undefined; 'kibana.alert.threshold_result.from'?: string | number | undefined; 'kibana.alert.threshold_result.terms'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.trigger'?: unknown; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" + "{ '@timestamp': string | number; 'kibana.alert.ancestors': { depth: string | number; id: string; index: string; type: string; }[]; 'kibana.alert.depth': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.original_event.action': string; 'kibana.alert.original_event.category': string[]; 'kibana.alert.original_event.created': string | number; 'kibana.alert.original_event.dataset': string; 'kibana.alert.original_event.id': string; 'kibana.alert.original_event.ingested': string | number; 'kibana.alert.original_event.kind': string; 'kibana.alert.original_event.module': string; 'kibana.alert.original_event.original': string; 'kibana.alert.original_event.outcome': string; 'kibana.alert.original_event.provider': string; 'kibana.alert.original_event.sequence': string | number; 'kibana.alert.original_event.type': string[]; 'kibana.alert.original_time': string | number; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.false_positives': string[]; 'kibana.alert.rule.max_signals': (string | number)[]; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.threat.framework': string; 'kibana.alert.rule.threat.tactic.id': string; 'kibana.alert.rule.threat.tactic.name': string; 'kibana.alert.rule.threat.tactic.reference': string; 'kibana.alert.rule.threat.technique.id': string; 'kibana.alert.rule.threat.technique.name': string; 'kibana.alert.rule.threat.technique.reference': string; 'kibana.alert.rule.threat.technique.subtechnique.id': string; 'kibana.alert.rule.threat.technique.subtechnique.name': string; 'kibana.alert.rule.threat.technique.subtechnique.reference': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'ecs.version'?: string | undefined; 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.ancestors.rule'?: string | undefined; 'kibana.alert.building_block_type'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.group.id'?: string | undefined; 'kibana.alert.group.index'?: number | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.new_terms'?: string[] | undefined; 'kibana.alert.original_event.agent_id_status'?: string | undefined; 'kibana.alert.original_event.code'?: string | undefined; 'kibana.alert.original_event.duration'?: string | undefined; 'kibana.alert.original_event.end'?: string | number | undefined; 'kibana.alert.original_event.hash'?: string | undefined; 'kibana.alert.original_event.reason'?: string | undefined; 'kibana.alert.original_event.reference'?: string | undefined; 'kibana.alert.original_event.risk_score'?: number | undefined; 'kibana.alert.original_event.risk_score_norm'?: number | undefined; 'kibana.alert.original_event.severity'?: string | number | undefined; 'kibana.alert.original_event.start'?: string | number | undefined; 'kibana.alert.original_event.timezone'?: string | undefined; 'kibana.alert.original_event.url'?: string | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.building_block_type'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | undefined; 'kibana.alert.rule.created_by'?: string | undefined; 'kibana.alert.rule.description'?: string | undefined; 'kibana.alert.rule.enabled'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.from'?: string | undefined; 'kibana.alert.rule.immutable'?: string[] | undefined; 'kibana.alert.rule.interval'?: string | undefined; 'kibana.alert.rule.license'?: string | undefined; 'kibana.alert.rule.note'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; '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.timeline_id'?: string[] | undefined; 'kibana.alert.rule.timeline_title'?: string[] | undefined; 'kibana.alert.rule.timestamp_override'?: string | undefined; 'kibana.alert.rule.to'?: string | undefined; 'kibana.alert.rule.type'?: string | undefined; 'kibana.alert.rule.updated_at'?: string | number | undefined; 'kibana.alert.rule.updated_by'?: string | undefined; 'kibana.alert.rule.version'?: string | undefined; 'kibana.alert.severity'?: string | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.suppression.docs_count'?: string | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: string[] | undefined; 'kibana.alert.suppression.terms.value'?: string[] | undefined; 'kibana.alert.system_status'?: string | undefined; 'kibana.alert.threshold_result.cardinality'?: unknown; 'kibana.alert.threshold_result.count'?: string | number | undefined; 'kibana.alert.threshold_result.from'?: string | number | undefined; 'kibana.alert.threshold_result.terms'?: { field?: string | undefined; value?: string | undefined; }[] | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_reason'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.alert.workflow_user'?: string | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; } & { '@timestamp': string | number; 'ecs.version': string; } & { 'agent.build.original'?: string | undefined; 'agent.ephemeral_id'?: string | undefined; 'agent.id'?: string | undefined; 'agent.name'?: string | undefined; 'agent.type'?: string | undefined; 'agent.version'?: string | undefined; 'client.address'?: string | undefined; 'client.as.number'?: string | number | undefined; 'client.as.organization.name'?: string | undefined; 'client.bytes'?: string | number | undefined; 'client.domain'?: string | undefined; 'client.geo.city_name'?: string | undefined; 'client.geo.continent_code'?: string | undefined; 'client.geo.continent_name'?: string | undefined; 'client.geo.country_iso_code'?: string | undefined; 'client.geo.country_name'?: string | undefined; 'client.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'client.geo.name'?: string | undefined; 'client.geo.postal_code'?: string | undefined; 'client.geo.region_iso_code'?: string | undefined; 'client.geo.region_name'?: string | undefined; 'client.geo.timezone'?: string | undefined; 'client.ip'?: string | undefined; 'client.mac'?: string | undefined; 'client.nat.ip'?: string | undefined; 'client.nat.port'?: string | number | undefined; 'client.packets'?: string | number | undefined; 'client.port'?: string | number | undefined; 'client.registered_domain'?: string | undefined; 'client.subdomain'?: string | undefined; 'client.top_level_domain'?: string | undefined; 'client.user.domain'?: string | undefined; 'client.user.email'?: string | undefined; 'client.user.full_name'?: string | undefined; 'client.user.group.domain'?: string | undefined; 'client.user.group.id'?: string | undefined; 'client.user.group.name'?: string | undefined; 'client.user.hash'?: string | undefined; 'client.user.id'?: string | undefined; 'client.user.name'?: string | undefined; 'client.user.roles'?: string[] | undefined; 'cloud.account.id'?: string | undefined; 'cloud.account.name'?: string | undefined; 'cloud.availability_zone'?: string | undefined; 'cloud.instance.id'?: string | undefined; 'cloud.instance.name'?: string | undefined; 'cloud.machine.type'?: string | undefined; 'cloud.origin.account.id'?: string | undefined; 'cloud.origin.account.name'?: string | undefined; 'cloud.origin.availability_zone'?: string | undefined; 'cloud.origin.instance.id'?: string | undefined; 'cloud.origin.instance.name'?: string | undefined; 'cloud.origin.machine.type'?: string | undefined; 'cloud.origin.project.id'?: string | undefined; 'cloud.origin.project.name'?: string | undefined; 'cloud.origin.provider'?: string | undefined; 'cloud.origin.region'?: string | undefined; 'cloud.origin.service.name'?: string | undefined; 'cloud.project.id'?: string | undefined; 'cloud.project.name'?: string | undefined; 'cloud.provider'?: string | undefined; 'cloud.region'?: string | undefined; 'cloud.service.name'?: string | undefined; 'cloud.target.account.id'?: string | undefined; 'cloud.target.account.name'?: string | undefined; 'cloud.target.availability_zone'?: string | undefined; 'cloud.target.instance.id'?: string | undefined; 'cloud.target.instance.name'?: string | undefined; 'cloud.target.machine.type'?: string | undefined; 'cloud.target.project.id'?: string | undefined; 'cloud.target.project.name'?: string | undefined; 'cloud.target.provider'?: string | undefined; 'cloud.target.region'?: string | undefined; 'cloud.target.service.name'?: string | undefined; 'container.cpu.usage'?: string | number | undefined; 'container.disk.read.bytes'?: string | number | undefined; 'container.disk.write.bytes'?: string | number | undefined; 'container.id'?: string | undefined; 'container.image.hash.all'?: string[] | undefined; 'container.image.name'?: string | undefined; 'container.image.tag'?: string[] | undefined; 'container.labels'?: unknown; 'container.memory.usage'?: string | number | undefined; 'container.name'?: string | undefined; 'container.network.egress.bytes'?: string | number | undefined; 'container.network.ingress.bytes'?: string | number | undefined; 'container.runtime'?: string | undefined; 'destination.address'?: string | undefined; 'destination.as.number'?: string | number | undefined; 'destination.as.organization.name'?: string | undefined; 'destination.bytes'?: string | number | undefined; 'destination.domain'?: string | undefined; 'destination.geo.city_name'?: string | undefined; 'destination.geo.continent_code'?: string | undefined; 'destination.geo.continent_name'?: string | undefined; 'destination.geo.country_iso_code'?: string | undefined; 'destination.geo.country_name'?: string | undefined; 'destination.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'destination.geo.name'?: string | undefined; 'destination.geo.postal_code'?: string | undefined; 'destination.geo.region_iso_code'?: string | undefined; 'destination.geo.region_name'?: string | undefined; 'destination.geo.timezone'?: string | undefined; 'destination.ip'?: string | undefined; 'destination.mac'?: string | undefined; 'destination.nat.ip'?: string | undefined; 'destination.nat.port'?: string | number | undefined; 'destination.packets'?: string | number | undefined; 'destination.port'?: string | number | undefined; 'destination.registered_domain'?: string | undefined; 'destination.subdomain'?: string | undefined; 'destination.top_level_domain'?: string | undefined; 'destination.user.domain'?: string | undefined; 'destination.user.email'?: string | undefined; 'destination.user.full_name'?: string | undefined; 'destination.user.group.domain'?: string | undefined; 'destination.user.group.id'?: string | undefined; 'destination.user.group.name'?: string | undefined; 'destination.user.hash'?: string | undefined; 'destination.user.id'?: string | undefined; 'destination.user.name'?: string | undefined; 'destination.user.roles'?: string[] | undefined; 'device.id'?: string | undefined; 'device.manufacturer'?: string | undefined; 'device.model.identifier'?: string | undefined; 'device.model.name'?: string | undefined; 'dll.code_signature.digest_algorithm'?: string | undefined; 'dll.code_signature.exists'?: boolean | undefined; 'dll.code_signature.signing_id'?: string | undefined; 'dll.code_signature.status'?: string | undefined; 'dll.code_signature.subject_name'?: string | undefined; 'dll.code_signature.team_id'?: string | undefined; 'dll.code_signature.timestamp'?: string | number | undefined; 'dll.code_signature.trusted'?: boolean | undefined; 'dll.code_signature.valid'?: boolean | undefined; 'dll.hash.md5'?: string | undefined; 'dll.hash.sha1'?: string | undefined; 'dll.hash.sha256'?: string | undefined; 'dll.hash.sha384'?: string | undefined; 'dll.hash.sha512'?: string | undefined; 'dll.hash.ssdeep'?: string | undefined; 'dll.hash.tlsh'?: string | undefined; 'dll.name'?: string | undefined; 'dll.path'?: string | undefined; 'dll.pe.architecture'?: string | undefined; 'dll.pe.company'?: string | undefined; 'dll.pe.description'?: string | undefined; 'dll.pe.file_version'?: string | undefined; 'dll.pe.imphash'?: string | undefined; 'dll.pe.original_file_name'?: string | undefined; 'dll.pe.pehash'?: string | undefined; 'dll.pe.product'?: string | undefined; 'dns.answers'?: { class?: string | undefined; data?: string | undefined; name?: string | undefined; ttl?: string | number | undefined; type?: string | undefined; }[] | undefined; 'dns.header_flags'?: string[] | undefined; 'dns.id'?: string | undefined; 'dns.op_code'?: string | undefined; 'dns.question.class'?: string | undefined; 'dns.question.name'?: string | undefined; 'dns.question.registered_domain'?: string | undefined; 'dns.question.subdomain'?: string | undefined; 'dns.question.top_level_domain'?: string | undefined; 'dns.question.type'?: string | undefined; 'dns.resolved_ip'?: string[] | undefined; 'dns.response_code'?: string | undefined; 'dns.type'?: string | undefined; 'email.attachments'?: { 'file.extension'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.name'?: string | undefined; 'file.size'?: string | number | undefined; }[] | undefined; 'email.bcc.address'?: string[] | undefined; 'email.cc.address'?: string[] | undefined; 'email.content_type'?: string | undefined; 'email.delivery_timestamp'?: string | number | undefined; 'email.direction'?: string | undefined; 'email.from.address'?: string[] | undefined; 'email.local_id'?: string | undefined; 'email.message_id'?: string | undefined; 'email.origination_timestamp'?: string | number | undefined; 'email.reply_to.address'?: string[] | undefined; 'email.sender.address'?: string | undefined; 'email.subject'?: string | undefined; 'email.to.address'?: string[] | undefined; 'email.x_mailer'?: string | undefined; 'error.code'?: string | undefined; 'error.id'?: string | undefined; 'error.message'?: string | undefined; 'error.stack_trace'?: string | undefined; 'error.type'?: string | undefined; 'event.action'?: string | undefined; 'event.agent_id_status'?: string | undefined; 'event.category'?: string[] | undefined; 'event.code'?: string | undefined; 'event.created'?: string | number | undefined; 'event.dataset'?: string | undefined; 'event.duration'?: string | number | undefined; 'event.end'?: string | number | undefined; 'event.hash'?: string | undefined; 'event.id'?: string | undefined; 'event.ingested'?: string | number | undefined; 'event.kind'?: string | undefined; 'event.module'?: string | undefined; 'event.original'?: string | undefined; 'event.outcome'?: string | undefined; 'event.provider'?: string | undefined; 'event.reason'?: string | undefined; 'event.reference'?: string | undefined; 'event.risk_score'?: number | undefined; 'event.risk_score_norm'?: number | undefined; 'event.sequence'?: string | number | undefined; 'event.severity'?: string | number | undefined; 'event.start'?: string | number | undefined; 'event.timezone'?: string | undefined; 'event.type'?: string[] | undefined; 'event.url'?: string | undefined; 'faas.coldstart'?: boolean | undefined; 'faas.execution'?: string | undefined; 'faas.id'?: string | undefined; 'faas.name'?: string | undefined; 'faas.version'?: string | undefined; 'file.accessed'?: string | number | undefined; 'file.attributes'?: string[] | undefined; 'file.code_signature.digest_algorithm'?: string | undefined; 'file.code_signature.exists'?: boolean | undefined; 'file.code_signature.signing_id'?: string | undefined; 'file.code_signature.status'?: string | undefined; 'file.code_signature.subject_name'?: string | undefined; 'file.code_signature.team_id'?: string | undefined; 'file.code_signature.timestamp'?: string | number | undefined; 'file.code_signature.trusted'?: boolean | undefined; 'file.code_signature.valid'?: boolean | undefined; 'file.created'?: string | number | undefined; 'file.ctime'?: string | number | undefined; 'file.device'?: string | undefined; 'file.directory'?: string | undefined; 'file.drive_letter'?: string | undefined; 'file.elf.architecture'?: string | undefined; 'file.elf.byte_order'?: string | undefined; 'file.elf.cpu_type'?: string | undefined; 'file.elf.creation_date'?: string | number | undefined; 'file.elf.exports'?: unknown[] | undefined; 'file.elf.header.abi_version'?: string | undefined; 'file.elf.header.class'?: string | undefined; 'file.elf.header.data'?: string | undefined; 'file.elf.header.entrypoint'?: string | number | undefined; 'file.elf.header.object_version'?: string | undefined; 'file.elf.header.os_abi'?: string | undefined; 'file.elf.header.type'?: string | undefined; 'file.elf.header.version'?: string | undefined; 'file.elf.imports'?: unknown[] | undefined; 'file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'file.elf.shared_libraries'?: string[] | undefined; 'file.elf.telfhash'?: string | undefined; 'file.extension'?: string | undefined; 'file.fork_name'?: string | undefined; 'file.gid'?: string | undefined; 'file.group'?: string | undefined; 'file.hash.md5'?: string | undefined; 'file.hash.sha1'?: string | undefined; 'file.hash.sha256'?: string | undefined; 'file.hash.sha384'?: string | undefined; 'file.hash.sha512'?: string | undefined; 'file.hash.ssdeep'?: string | undefined; 'file.hash.tlsh'?: string | undefined; 'file.inode'?: string | undefined; 'file.mime_type'?: string | undefined; 'file.mode'?: string | undefined; 'file.mtime'?: string | number | undefined; 'file.name'?: string | undefined; 'file.owner'?: string | undefined; 'file.path'?: string | undefined; 'file.pe.architecture'?: string | undefined; 'file.pe.company'?: string | undefined; 'file.pe.description'?: string | undefined; 'file.pe.file_version'?: string | undefined; 'file.pe.imphash'?: string | undefined; 'file.pe.original_file_name'?: string | undefined; 'file.pe.pehash'?: string | undefined; 'file.pe.product'?: string | undefined; 'file.size'?: string | number | undefined; 'file.target_path'?: string | undefined; 'file.type'?: string | undefined; 'file.uid'?: string | undefined; 'file.x509.alternative_names'?: string[] | undefined; 'file.x509.issuer.common_name'?: string[] | undefined; 'file.x509.issuer.country'?: string[] | undefined; 'file.x509.issuer.distinguished_name'?: string | undefined; 'file.x509.issuer.locality'?: string[] | undefined; 'file.x509.issuer.organization'?: string[] | undefined; 'file.x509.issuer.organizational_unit'?: string[] | undefined; 'file.x509.issuer.state_or_province'?: string[] | undefined; 'file.x509.not_after'?: string | number | undefined; 'file.x509.not_before'?: string | number | undefined; 'file.x509.public_key_algorithm'?: string | undefined; 'file.x509.public_key_curve'?: string | undefined; 'file.x509.public_key_exponent'?: string | number | undefined; 'file.x509.public_key_size'?: string | number | undefined; 'file.x509.serial_number'?: string | undefined; 'file.x509.signature_algorithm'?: string | undefined; 'file.x509.subject.common_name'?: string[] | undefined; 'file.x509.subject.country'?: string[] | undefined; 'file.x509.subject.distinguished_name'?: string | undefined; 'file.x509.subject.locality'?: string[] | undefined; 'file.x509.subject.organization'?: string[] | undefined; 'file.x509.subject.organizational_unit'?: string[] | undefined; 'file.x509.subject.state_or_province'?: string[] | undefined; 'file.x509.version_number'?: string | undefined; 'group.domain'?: string | undefined; 'group.id'?: string | undefined; 'group.name'?: string | undefined; 'host.architecture'?: string | undefined; 'host.boot.id'?: string | undefined; 'host.cpu.usage'?: string | number | undefined; 'host.disk.read.bytes'?: string | number | undefined; 'host.disk.write.bytes'?: string | number | undefined; 'host.domain'?: string | undefined; 'host.geo.city_name'?: string | undefined; 'host.geo.continent_code'?: string | undefined; 'host.geo.continent_name'?: string | undefined; 'host.geo.country_iso_code'?: string | undefined; 'host.geo.country_name'?: string | undefined; 'host.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'host.geo.name'?: string | undefined; 'host.geo.postal_code'?: string | undefined; 'host.geo.region_iso_code'?: string | undefined; 'host.geo.region_name'?: string | undefined; 'host.geo.timezone'?: string | undefined; 'host.hostname'?: string | undefined; 'host.id'?: string | undefined; 'host.ip'?: string[] | undefined; 'host.mac'?: string[] | undefined; 'host.name'?: string | undefined; 'host.network.egress.bytes'?: string | number | undefined; 'host.network.egress.packets'?: string | number | undefined; 'host.network.ingress.bytes'?: string | number | undefined; 'host.network.ingress.packets'?: string | number | undefined; 'host.os.family'?: string | undefined; 'host.os.full'?: string | undefined; 'host.os.kernel'?: string | undefined; 'host.os.name'?: string | undefined; 'host.os.platform'?: string | undefined; 'host.os.type'?: string | undefined; 'host.os.version'?: string | undefined; 'host.pid_ns_ino'?: string | undefined; 'host.risk.calculated_level'?: string | undefined; 'host.risk.calculated_score'?: number | undefined; 'host.risk.calculated_score_norm'?: number | undefined; 'host.risk.static_level'?: string | undefined; 'host.risk.static_score'?: number | undefined; 'host.risk.static_score_norm'?: number | undefined; 'host.type'?: string | undefined; 'host.uptime'?: string | number | undefined; 'http.request.body.bytes'?: string | number | undefined; 'http.request.body.content'?: string | undefined; 'http.request.bytes'?: string | number | undefined; 'http.request.id'?: string | undefined; 'http.request.method'?: string | undefined; 'http.request.mime_type'?: string | undefined; 'http.request.referrer'?: string | undefined; 'http.response.body.bytes'?: string | number | undefined; 'http.response.body.content'?: string | undefined; 'http.response.bytes'?: string | number | undefined; 'http.response.mime_type'?: string | undefined; 'http.response.status_code'?: string | number | undefined; 'http.version'?: string | undefined; labels?: unknown; 'log.file.path'?: string | undefined; 'log.level'?: string | undefined; 'log.logger'?: string | undefined; 'log.origin.file.line'?: string | number | undefined; 'log.origin.file.name'?: string | undefined; 'log.origin.function'?: string | undefined; 'log.syslog'?: unknown; message?: string | undefined; 'network.application'?: string | undefined; 'network.bytes'?: string | number | undefined; 'network.community_id'?: string | undefined; 'network.direction'?: string | undefined; 'network.forwarded_ip'?: string | undefined; 'network.iana_number'?: string | undefined; 'network.inner'?: unknown; 'network.name'?: string | undefined; 'network.packets'?: string | number | undefined; 'network.protocol'?: string | undefined; 'network.transport'?: string | undefined; 'network.type'?: string | undefined; 'network.vlan.id'?: string | undefined; 'network.vlan.name'?: string | undefined; 'observer.egress'?: unknown; 'observer.geo.city_name'?: string | undefined; 'observer.geo.continent_code'?: string | undefined; 'observer.geo.continent_name'?: string | undefined; 'observer.geo.country_iso_code'?: string | undefined; 'observer.geo.country_name'?: string | undefined; 'observer.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'observer.geo.name'?: string | undefined; 'observer.geo.postal_code'?: string | undefined; 'observer.geo.region_iso_code'?: string | undefined; 'observer.geo.region_name'?: string | undefined; 'observer.geo.timezone'?: string | undefined; 'observer.hostname'?: string | undefined; 'observer.ingress'?: unknown; 'observer.ip'?: string[] | undefined; 'observer.mac'?: string[] | undefined; 'observer.name'?: string | undefined; 'observer.os.family'?: string | undefined; 'observer.os.full'?: string | undefined; 'observer.os.kernel'?: string | undefined; 'observer.os.name'?: string | undefined; 'observer.os.platform'?: string | undefined; 'observer.os.type'?: string | undefined; 'observer.os.version'?: string | undefined; 'observer.product'?: string | undefined; 'observer.serial_number'?: string | undefined; 'observer.type'?: string | undefined; 'observer.vendor'?: string | undefined; 'observer.version'?: string | undefined; 'orchestrator.api_version'?: string | undefined; 'orchestrator.cluster.id'?: string | undefined; 'orchestrator.cluster.name'?: string | undefined; 'orchestrator.cluster.url'?: string | undefined; 'orchestrator.cluster.version'?: string | undefined; 'orchestrator.namespace'?: string | undefined; 'orchestrator.organization'?: string | undefined; 'orchestrator.resource.id'?: string | undefined; 'orchestrator.resource.ip'?: string[] | undefined; 'orchestrator.resource.name'?: string | undefined; 'orchestrator.resource.parent.type'?: string | undefined; 'orchestrator.resource.type'?: string | undefined; 'orchestrator.type'?: string | undefined; 'organization.id'?: string | undefined; 'organization.name'?: string | undefined; 'package.architecture'?: string | undefined; 'package.build_version'?: string | undefined; 'package.checksum'?: string | undefined; 'package.description'?: string | undefined; 'package.install_scope'?: string | undefined; 'package.installed'?: string | number | undefined; 'package.license'?: string | undefined; 'package.name'?: string | undefined; 'package.path'?: string | undefined; 'package.reference'?: string | undefined; 'package.size'?: string | number | undefined; 'package.type'?: string | undefined; 'package.version'?: string | undefined; 'process.args'?: string[] | undefined; 'process.args_count'?: string | number | undefined; 'process.code_signature.digest_algorithm'?: string | undefined; 'process.code_signature.exists'?: boolean | undefined; 'process.code_signature.signing_id'?: string | undefined; 'process.code_signature.status'?: string | undefined; 'process.code_signature.subject_name'?: string | undefined; 'process.code_signature.team_id'?: string | undefined; 'process.code_signature.timestamp'?: string | number | undefined; 'process.code_signature.trusted'?: boolean | undefined; 'process.code_signature.valid'?: boolean | undefined; 'process.command_line'?: string | undefined; 'process.elf.architecture'?: string | undefined; 'process.elf.byte_order'?: string | undefined; 'process.elf.cpu_type'?: string | undefined; 'process.elf.creation_date'?: string | number | undefined; 'process.elf.exports'?: unknown[] | undefined; 'process.elf.header.abi_version'?: string | undefined; 'process.elf.header.class'?: string | undefined; 'process.elf.header.data'?: string | undefined; 'process.elf.header.entrypoint'?: string | number | undefined; 'process.elf.header.object_version'?: string | undefined; 'process.elf.header.os_abi'?: string | undefined; 'process.elf.header.type'?: string | undefined; 'process.elf.header.version'?: string | undefined; 'process.elf.imports'?: unknown[] | undefined; 'process.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.elf.shared_libraries'?: string[] | undefined; 'process.elf.telfhash'?: string | undefined; 'process.end'?: string | number | undefined; 'process.entity_id'?: string | undefined; 'process.entry_leader.args'?: string[] | undefined; 'process.entry_leader.args_count'?: string | number | undefined; 'process.entry_leader.attested_groups.name'?: string | undefined; 'process.entry_leader.attested_user.id'?: string | undefined; 'process.entry_leader.attested_user.name'?: string | undefined; 'process.entry_leader.command_line'?: string | undefined; 'process.entry_leader.entity_id'?: string | undefined; 'process.entry_leader.entry_meta.source.ip'?: string | undefined; 'process.entry_leader.entry_meta.type'?: string | undefined; 'process.entry_leader.executable'?: string | undefined; 'process.entry_leader.group.id'?: string | undefined; 'process.entry_leader.group.name'?: string | undefined; 'process.entry_leader.interactive'?: boolean | undefined; 'process.entry_leader.name'?: string | undefined; 'process.entry_leader.parent.entity_id'?: string | undefined; 'process.entry_leader.parent.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.entity_id'?: string | undefined; 'process.entry_leader.parent.session_leader.pid'?: string | number | undefined; 'process.entry_leader.parent.session_leader.start'?: string | number | undefined; 'process.entry_leader.parent.start'?: string | number | undefined; 'process.entry_leader.pid'?: string | number | undefined; 'process.entry_leader.real_group.id'?: string | undefined; 'process.entry_leader.real_group.name'?: string | undefined; 'process.entry_leader.real_user.id'?: string | undefined; 'process.entry_leader.real_user.name'?: string | undefined; 'process.entry_leader.same_as_process'?: boolean | undefined; 'process.entry_leader.saved_group.id'?: string | undefined; 'process.entry_leader.saved_group.name'?: string | undefined; 'process.entry_leader.saved_user.id'?: string | undefined; 'process.entry_leader.saved_user.name'?: string | undefined; 'process.entry_leader.start'?: string | number | undefined; 'process.entry_leader.supplemental_groups.id'?: string | undefined; 'process.entry_leader.supplemental_groups.name'?: string | undefined; 'process.entry_leader.tty'?: unknown; 'process.entry_leader.user.id'?: string | undefined; 'process.entry_leader.user.name'?: string | undefined; 'process.entry_leader.working_directory'?: string | undefined; 'process.env_vars'?: string[] | undefined; 'process.executable'?: string | undefined; 'process.exit_code'?: string | number | undefined; 'process.group_leader.args'?: string[] | undefined; 'process.group_leader.args_count'?: string | number | undefined; 'process.group_leader.command_line'?: string | undefined; 'process.group_leader.entity_id'?: string | undefined; 'process.group_leader.executable'?: string | undefined; 'process.group_leader.group.id'?: string | undefined; 'process.group_leader.group.name'?: string | undefined; 'process.group_leader.interactive'?: boolean | undefined; 'process.group_leader.name'?: string | undefined; 'process.group_leader.pid'?: string | number | undefined; 'process.group_leader.real_group.id'?: string | undefined; 'process.group_leader.real_group.name'?: string | undefined; 'process.group_leader.real_user.id'?: string | undefined; 'process.group_leader.real_user.name'?: string | undefined; 'process.group_leader.same_as_process'?: boolean | undefined; 'process.group_leader.saved_group.id'?: string | undefined; 'process.group_leader.saved_group.name'?: string | undefined; 'process.group_leader.saved_user.id'?: string | undefined; 'process.group_leader.saved_user.name'?: string | undefined; 'process.group_leader.start'?: string | number | undefined; 'process.group_leader.supplemental_groups.id'?: string | undefined; 'process.group_leader.supplemental_groups.name'?: string | undefined; 'process.group_leader.tty'?: unknown; 'process.group_leader.user.id'?: string | undefined; 'process.group_leader.user.name'?: string | undefined; 'process.group_leader.working_directory'?: string | undefined; 'process.hash.md5'?: string | undefined; 'process.hash.sha1'?: string | undefined; 'process.hash.sha256'?: string | undefined; 'process.hash.sha384'?: string | undefined; 'process.hash.sha512'?: string | undefined; 'process.hash.ssdeep'?: string | undefined; 'process.hash.tlsh'?: string | undefined; 'process.interactive'?: boolean | undefined; 'process.io'?: unknown; 'process.name'?: string | undefined; 'process.parent.args'?: string[] | undefined; 'process.parent.args_count'?: string | number | undefined; 'process.parent.code_signature.digest_algorithm'?: string | undefined; 'process.parent.code_signature.exists'?: boolean | undefined; 'process.parent.code_signature.signing_id'?: string | undefined; 'process.parent.code_signature.status'?: string | undefined; 'process.parent.code_signature.subject_name'?: string | undefined; 'process.parent.code_signature.team_id'?: string | undefined; 'process.parent.code_signature.timestamp'?: string | number | undefined; 'process.parent.code_signature.trusted'?: boolean | undefined; 'process.parent.code_signature.valid'?: boolean | undefined; 'process.parent.command_line'?: string | undefined; 'process.parent.elf.architecture'?: string | undefined; 'process.parent.elf.byte_order'?: string | undefined; 'process.parent.elf.cpu_type'?: string | undefined; 'process.parent.elf.creation_date'?: string | number | undefined; 'process.parent.elf.exports'?: unknown[] | undefined; 'process.parent.elf.header.abi_version'?: string | undefined; 'process.parent.elf.header.class'?: string | undefined; 'process.parent.elf.header.data'?: string | undefined; 'process.parent.elf.header.entrypoint'?: string | number | undefined; 'process.parent.elf.header.object_version'?: string | undefined; 'process.parent.elf.header.os_abi'?: string | undefined; 'process.parent.elf.header.type'?: string | undefined; 'process.parent.elf.header.version'?: string | undefined; 'process.parent.elf.imports'?: unknown[] | undefined; 'process.parent.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'process.parent.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'process.parent.elf.shared_libraries'?: string[] | undefined; 'process.parent.elf.telfhash'?: string | undefined; 'process.parent.end'?: string | number | undefined; 'process.parent.entity_id'?: string | undefined; 'process.parent.executable'?: string | undefined; 'process.parent.exit_code'?: string | number | undefined; 'process.parent.group.id'?: string | undefined; 'process.parent.group.name'?: string | undefined; 'process.parent.group_leader.entity_id'?: string | undefined; 'process.parent.group_leader.pid'?: string | number | undefined; 'process.parent.group_leader.start'?: string | number | undefined; 'process.parent.hash.md5'?: string | undefined; 'process.parent.hash.sha1'?: string | undefined; 'process.parent.hash.sha256'?: string | undefined; 'process.parent.hash.sha384'?: string | undefined; 'process.parent.hash.sha512'?: string | undefined; 'process.parent.hash.ssdeep'?: string | undefined; 'process.parent.hash.tlsh'?: string | undefined; 'process.parent.interactive'?: boolean | undefined; 'process.parent.name'?: string | undefined; 'process.parent.pe.architecture'?: string | undefined; 'process.parent.pe.company'?: string | undefined; 'process.parent.pe.description'?: string | undefined; 'process.parent.pe.file_version'?: string | undefined; 'process.parent.pe.imphash'?: string | undefined; 'process.parent.pe.original_file_name'?: string | undefined; 'process.parent.pe.pehash'?: string | undefined; 'process.parent.pe.product'?: string | undefined; 'process.parent.pgid'?: string | number | undefined; 'process.parent.pid'?: string | number | undefined; 'process.parent.real_group.id'?: string | undefined; 'process.parent.real_group.name'?: string | undefined; 'process.parent.real_user.id'?: string | undefined; 'process.parent.real_user.name'?: string | undefined; 'process.parent.saved_group.id'?: string | undefined; 'process.parent.saved_group.name'?: string | undefined; 'process.parent.saved_user.id'?: string | undefined; 'process.parent.saved_user.name'?: string | undefined; 'process.parent.start'?: string | number | undefined; 'process.parent.supplemental_groups.id'?: string | undefined; 'process.parent.supplemental_groups.name'?: string | undefined; 'process.parent.thread.id'?: string | number | undefined; 'process.parent.thread.name'?: string | undefined; 'process.parent.title'?: string | undefined; 'process.parent.tty'?: unknown; 'process.parent.uptime'?: string | number | undefined; 'process.parent.user.id'?: string | undefined; 'process.parent.user.name'?: string | undefined; 'process.parent.working_directory'?: string | undefined; 'process.pe.architecture'?: string | undefined; 'process.pe.company'?: string | undefined; 'process.pe.description'?: string | undefined; 'process.pe.file_version'?: string | undefined; 'process.pe.imphash'?: string | undefined; 'process.pe.original_file_name'?: string | undefined; 'process.pe.pehash'?: string | undefined; 'process.pe.product'?: string | undefined; 'process.pgid'?: string | number | undefined; 'process.pid'?: string | number | undefined; 'process.previous.args'?: string[] | undefined; 'process.previous.args_count'?: string | number | undefined; 'process.previous.executable'?: string | undefined; 'process.real_group.id'?: string | undefined; 'process.real_group.name'?: string | undefined; 'process.real_user.id'?: string | undefined; 'process.real_user.name'?: string | undefined; 'process.saved_group.id'?: string | undefined; 'process.saved_group.name'?: string | undefined; 'process.saved_user.id'?: string | undefined; 'process.saved_user.name'?: string | undefined; 'process.session_leader.args'?: string[] | undefined; 'process.session_leader.args_count'?: string | number | undefined; 'process.session_leader.command_line'?: string | undefined; 'process.session_leader.entity_id'?: string | undefined; 'process.session_leader.executable'?: string | undefined; 'process.session_leader.group.id'?: string | undefined; 'process.session_leader.group.name'?: string | undefined; 'process.session_leader.interactive'?: boolean | undefined; 'process.session_leader.name'?: string | undefined; 'process.session_leader.parent.entity_id'?: string | undefined; 'process.session_leader.parent.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.entity_id'?: string | undefined; 'process.session_leader.parent.session_leader.pid'?: string | number | undefined; 'process.session_leader.parent.session_leader.start'?: string | number | undefined; 'process.session_leader.parent.start'?: string | number | undefined; 'process.session_leader.pid'?: string | number | undefined; 'process.session_leader.real_group.id'?: string | undefined; 'process.session_leader.real_group.name'?: string | undefined; 'process.session_leader.real_user.id'?: string | undefined; 'process.session_leader.real_user.name'?: string | undefined; 'process.session_leader.same_as_process'?: boolean | undefined; 'process.session_leader.saved_group.id'?: string | undefined; 'process.session_leader.saved_group.name'?: string | undefined; 'process.session_leader.saved_user.id'?: string | undefined; 'process.session_leader.saved_user.name'?: string | undefined; 'process.session_leader.start'?: string | number | undefined; 'process.session_leader.supplemental_groups.id'?: string | undefined; 'process.session_leader.supplemental_groups.name'?: string | undefined; 'process.session_leader.tty'?: unknown; 'process.session_leader.user.id'?: string | undefined; 'process.session_leader.user.name'?: string | undefined; 'process.session_leader.working_directory'?: string | undefined; 'process.start'?: string | number | undefined; 'process.supplemental_groups.id'?: string | undefined; 'process.supplemental_groups.name'?: string | undefined; 'process.thread.id'?: string | number | undefined; 'process.thread.name'?: string | undefined; 'process.title'?: string | undefined; 'process.tty'?: unknown; 'process.uptime'?: string | number | undefined; 'process.user.id'?: string | undefined; 'process.user.name'?: string | undefined; 'process.working_directory'?: string | undefined; 'registry.data.bytes'?: string | undefined; 'registry.data.strings'?: string[] | undefined; 'registry.data.type'?: string | undefined; 'registry.hive'?: string | undefined; 'registry.key'?: string | undefined; 'registry.path'?: string | undefined; 'registry.value'?: string | undefined; 'related.hash'?: string[] | undefined; 'related.hosts'?: string[] | undefined; 'related.ip'?: string[] | undefined; 'related.user'?: string[] | undefined; 'rule.author'?: string[] | undefined; 'rule.category'?: string | undefined; 'rule.description'?: string | undefined; 'rule.id'?: string | undefined; 'rule.license'?: string | undefined; 'rule.name'?: string | undefined; 'rule.reference'?: string | undefined; 'rule.ruleset'?: string | undefined; 'rule.uuid'?: string | undefined; 'rule.version'?: string | undefined; 'server.address'?: string | undefined; 'server.as.number'?: string | number | undefined; 'server.as.organization.name'?: string | undefined; 'server.bytes'?: string | number | undefined; 'server.domain'?: string | undefined; 'server.geo.city_name'?: string | undefined; 'server.geo.continent_code'?: string | undefined; 'server.geo.continent_name'?: string | undefined; 'server.geo.country_iso_code'?: string | undefined; 'server.geo.country_name'?: string | undefined; 'server.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'server.geo.name'?: string | undefined; 'server.geo.postal_code'?: string | undefined; 'server.geo.region_iso_code'?: string | undefined; 'server.geo.region_name'?: string | undefined; 'server.geo.timezone'?: string | undefined; 'server.ip'?: string | undefined; 'server.mac'?: string | undefined; 'server.nat.ip'?: string | undefined; 'server.nat.port'?: string | number | undefined; 'server.packets'?: string | number | undefined; 'server.port'?: string | number | undefined; 'server.registered_domain'?: string | undefined; 'server.subdomain'?: string | undefined; 'server.top_level_domain'?: string | undefined; 'server.user.domain'?: string | undefined; 'server.user.email'?: string | undefined; 'server.user.full_name'?: string | undefined; 'server.user.group.domain'?: string | undefined; 'server.user.group.id'?: string | undefined; 'server.user.group.name'?: string | undefined; 'server.user.hash'?: string | undefined; 'server.user.id'?: string | undefined; 'server.user.name'?: string | undefined; 'server.user.roles'?: string[] | undefined; 'service.address'?: string | undefined; 'service.environment'?: string | undefined; 'service.ephemeral_id'?: string | undefined; 'service.id'?: string | undefined; 'service.name'?: string | undefined; 'service.node.name'?: string | undefined; 'service.node.role'?: string | undefined; 'service.node.roles'?: string[] | undefined; 'service.origin.address'?: string | undefined; 'service.origin.environment'?: string | undefined; 'service.origin.ephemeral_id'?: string | undefined; 'service.origin.id'?: string | undefined; 'service.origin.name'?: string | undefined; 'service.origin.node.name'?: string | undefined; 'service.origin.node.role'?: string | undefined; 'service.origin.node.roles'?: string[] | undefined; 'service.origin.state'?: string | undefined; 'service.origin.type'?: string | undefined; 'service.origin.version'?: string | undefined; 'service.state'?: string | undefined; 'service.target.address'?: string | undefined; 'service.target.environment'?: string | undefined; 'service.target.ephemeral_id'?: string | undefined; 'service.target.id'?: string | undefined; 'service.target.name'?: string | undefined; 'service.target.node.name'?: string | undefined; 'service.target.node.role'?: string | undefined; 'service.target.node.roles'?: string[] | undefined; 'service.target.state'?: string | undefined; 'service.target.type'?: string | undefined; 'service.target.version'?: string | undefined; 'service.type'?: string | undefined; 'service.version'?: string | undefined; 'source.address'?: string | undefined; 'source.as.number'?: string | number | undefined; 'source.as.organization.name'?: string | undefined; 'source.bytes'?: string | number | undefined; 'source.domain'?: string | undefined; 'source.geo.city_name'?: string | undefined; 'source.geo.continent_code'?: string | undefined; 'source.geo.continent_name'?: string | undefined; 'source.geo.country_iso_code'?: string | undefined; 'source.geo.country_name'?: string | undefined; 'source.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'source.geo.name'?: string | undefined; 'source.geo.postal_code'?: string | undefined; 'source.geo.region_iso_code'?: string | undefined; 'source.geo.region_name'?: string | undefined; 'source.geo.timezone'?: string | undefined; 'source.ip'?: string | undefined; 'source.mac'?: string | undefined; 'source.nat.ip'?: string | undefined; 'source.nat.port'?: string | number | undefined; 'source.packets'?: string | number | undefined; 'source.port'?: string | number | undefined; 'source.registered_domain'?: string | undefined; 'source.subdomain'?: string | undefined; 'source.top_level_domain'?: string | undefined; 'source.user.domain'?: string | undefined; 'source.user.email'?: string | undefined; 'source.user.full_name'?: string | undefined; 'source.user.group.domain'?: string | undefined; 'source.user.group.id'?: string | undefined; 'source.user.group.name'?: string | undefined; 'source.user.hash'?: string | undefined; 'source.user.id'?: string | undefined; 'source.user.name'?: string | undefined; 'source.user.roles'?: string[] | undefined; 'span.id'?: string | undefined; tags?: string[] | undefined; 'threat.enrichments'?: { indicator?: unknown; 'matched.atomic'?: string | undefined; 'matched.field'?: string | undefined; 'matched.id'?: string | undefined; 'matched.index'?: string | undefined; 'matched.occurred'?: string | number | undefined; 'matched.type'?: string | undefined; }[] | undefined; 'threat.feed.dashboard_id'?: string | undefined; 'threat.feed.description'?: string | undefined; 'threat.feed.name'?: string | undefined; 'threat.feed.reference'?: string | undefined; 'threat.framework'?: string | undefined; 'threat.group.alias'?: string[] | undefined; 'threat.group.id'?: string | undefined; 'threat.group.name'?: string | undefined; 'threat.group.reference'?: string | undefined; 'threat.indicator.as.number'?: string | number | undefined; 'threat.indicator.as.organization.name'?: string | undefined; 'threat.indicator.confidence'?: string | undefined; 'threat.indicator.description'?: string | undefined; 'threat.indicator.email.address'?: string | undefined; 'threat.indicator.file.accessed'?: string | number | undefined; 'threat.indicator.file.attributes'?: string[] | undefined; 'threat.indicator.file.code_signature.digest_algorithm'?: string | undefined; 'threat.indicator.file.code_signature.exists'?: boolean | undefined; 'threat.indicator.file.code_signature.signing_id'?: string | undefined; 'threat.indicator.file.code_signature.status'?: string | undefined; 'threat.indicator.file.code_signature.subject_name'?: string | undefined; 'threat.indicator.file.code_signature.team_id'?: string | undefined; 'threat.indicator.file.code_signature.timestamp'?: string | number | undefined; 'threat.indicator.file.code_signature.trusted'?: boolean | undefined; 'threat.indicator.file.code_signature.valid'?: boolean | undefined; 'threat.indicator.file.created'?: string | number | undefined; 'threat.indicator.file.ctime'?: string | number | undefined; 'threat.indicator.file.device'?: string | undefined; 'threat.indicator.file.directory'?: string | undefined; 'threat.indicator.file.drive_letter'?: string | undefined; 'threat.indicator.file.elf.architecture'?: string | undefined; 'threat.indicator.file.elf.byte_order'?: string | undefined; 'threat.indicator.file.elf.cpu_type'?: string | undefined; 'threat.indicator.file.elf.creation_date'?: string | number | undefined; 'threat.indicator.file.elf.exports'?: unknown[] | undefined; 'threat.indicator.file.elf.header.abi_version'?: string | undefined; 'threat.indicator.file.elf.header.class'?: string | undefined; 'threat.indicator.file.elf.header.data'?: string | undefined; 'threat.indicator.file.elf.header.entrypoint'?: string | number | undefined; 'threat.indicator.file.elf.header.object_version'?: string | undefined; 'threat.indicator.file.elf.header.os_abi'?: string | undefined; 'threat.indicator.file.elf.header.type'?: string | undefined; 'threat.indicator.file.elf.header.version'?: string | undefined; 'threat.indicator.file.elf.imports'?: unknown[] | undefined; 'threat.indicator.file.elf.sections'?: { chi2?: string | number | undefined; entropy?: string | number | undefined; flags?: string | undefined; name?: string | undefined; physical_offset?: string | undefined; physical_size?: string | number | undefined; type?: string | undefined; virtual_address?: string | number | undefined; virtual_size?: string | number | undefined; }[] | undefined; 'threat.indicator.file.elf.segments'?: { sections?: string | undefined; type?: string | undefined; }[] | undefined; 'threat.indicator.file.elf.shared_libraries'?: string[] | undefined; 'threat.indicator.file.elf.telfhash'?: string | undefined; 'threat.indicator.file.extension'?: string | undefined; 'threat.indicator.file.fork_name'?: string | undefined; 'threat.indicator.file.gid'?: string | undefined; 'threat.indicator.file.group'?: string | undefined; 'threat.indicator.file.hash.md5'?: string | undefined; 'threat.indicator.file.hash.sha1'?: string | undefined; 'threat.indicator.file.hash.sha256'?: string | undefined; 'threat.indicator.file.hash.sha384'?: string | undefined; 'threat.indicator.file.hash.sha512'?: string | undefined; 'threat.indicator.file.hash.ssdeep'?: string | undefined; 'threat.indicator.file.hash.tlsh'?: string | undefined; 'threat.indicator.file.inode'?: string | undefined; 'threat.indicator.file.mime_type'?: string | undefined; 'threat.indicator.file.mode'?: string | undefined; 'threat.indicator.file.mtime'?: string | number | undefined; 'threat.indicator.file.name'?: string | undefined; 'threat.indicator.file.owner'?: string | undefined; 'threat.indicator.file.path'?: string | undefined; 'threat.indicator.file.pe.architecture'?: string | undefined; 'threat.indicator.file.pe.company'?: string | undefined; 'threat.indicator.file.pe.description'?: string | undefined; 'threat.indicator.file.pe.file_version'?: string | undefined; 'threat.indicator.file.pe.imphash'?: string | undefined; 'threat.indicator.file.pe.original_file_name'?: string | undefined; 'threat.indicator.file.pe.pehash'?: string | undefined; 'threat.indicator.file.pe.product'?: string | undefined; 'threat.indicator.file.size'?: string | number | undefined; 'threat.indicator.file.target_path'?: string | undefined; 'threat.indicator.file.type'?: string | undefined; 'threat.indicator.file.uid'?: string | undefined; 'threat.indicator.file.x509.alternative_names'?: string[] | undefined; 'threat.indicator.file.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.file.x509.issuer.country'?: string[] | undefined; 'threat.indicator.file.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.file.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.not_after'?: string | number | undefined; 'threat.indicator.file.x509.not_before'?: string | number | undefined; 'threat.indicator.file.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.file.x509.public_key_curve'?: string | undefined; 'threat.indicator.file.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.file.x509.public_key_size'?: string | number | undefined; 'threat.indicator.file.x509.serial_number'?: string | undefined; 'threat.indicator.file.x509.signature_algorithm'?: string | undefined; 'threat.indicator.file.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.file.x509.subject.country'?: string[] | undefined; 'threat.indicator.file.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.file.x509.subject.locality'?: string[] | undefined; 'threat.indicator.file.x509.subject.organization'?: string[] | undefined; 'threat.indicator.file.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.file.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.file.x509.version_number'?: string | undefined; 'threat.indicator.first_seen'?: string | number | undefined; 'threat.indicator.geo.city_name'?: string | undefined; 'threat.indicator.geo.continent_code'?: string | undefined; 'threat.indicator.geo.continent_name'?: string | undefined; 'threat.indicator.geo.country_iso_code'?: string | undefined; 'threat.indicator.geo.country_name'?: string | undefined; 'threat.indicator.geo.location'?: string | { type: string; coordinates: number[]; } | { lat: number; lon: number; } | { location: number[]; } | { location: string; } | undefined; 'threat.indicator.geo.name'?: string | undefined; 'threat.indicator.geo.postal_code'?: string | undefined; 'threat.indicator.geo.region_iso_code'?: string | undefined; 'threat.indicator.geo.region_name'?: string | undefined; 'threat.indicator.geo.timezone'?: string | undefined; 'threat.indicator.ip'?: string | undefined; 'threat.indicator.last_seen'?: string | number | undefined; 'threat.indicator.marking.tlp'?: string | undefined; 'threat.indicator.marking.tlp_version'?: string | undefined; 'threat.indicator.modified_at'?: string | number | undefined; 'threat.indicator.port'?: string | number | undefined; 'threat.indicator.provider'?: string | undefined; 'threat.indicator.reference'?: string | undefined; 'threat.indicator.registry.data.bytes'?: string | undefined; 'threat.indicator.registry.data.strings'?: string[] | undefined; 'threat.indicator.registry.data.type'?: string | undefined; 'threat.indicator.registry.hive'?: string | undefined; 'threat.indicator.registry.key'?: string | undefined; 'threat.indicator.registry.path'?: string | undefined; 'threat.indicator.registry.value'?: string | undefined; 'threat.indicator.scanner_stats'?: string | number | undefined; 'threat.indicator.sightings'?: string | number | undefined; 'threat.indicator.type'?: string | undefined; 'threat.indicator.url.domain'?: string | undefined; 'threat.indicator.url.extension'?: string | undefined; 'threat.indicator.url.fragment'?: string | undefined; 'threat.indicator.url.full'?: string | undefined; 'threat.indicator.url.original'?: string | undefined; 'threat.indicator.url.password'?: string | undefined; 'threat.indicator.url.path'?: string | undefined; 'threat.indicator.url.port'?: string | number | undefined; 'threat.indicator.url.query'?: string | undefined; 'threat.indicator.url.registered_domain'?: string | undefined; 'threat.indicator.url.scheme'?: string | undefined; 'threat.indicator.url.subdomain'?: string | undefined; 'threat.indicator.url.top_level_domain'?: string | undefined; 'threat.indicator.url.username'?: string | undefined; 'threat.indicator.x509.alternative_names'?: string[] | undefined; 'threat.indicator.x509.issuer.common_name'?: string[] | undefined; 'threat.indicator.x509.issuer.country'?: string[] | undefined; 'threat.indicator.x509.issuer.distinguished_name'?: string | undefined; 'threat.indicator.x509.issuer.locality'?: string[] | undefined; 'threat.indicator.x509.issuer.organization'?: string[] | undefined; 'threat.indicator.x509.issuer.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.issuer.state_or_province'?: string[] | undefined; 'threat.indicator.x509.not_after'?: string | number | undefined; 'threat.indicator.x509.not_before'?: string | number | undefined; 'threat.indicator.x509.public_key_algorithm'?: string | undefined; 'threat.indicator.x509.public_key_curve'?: string | undefined; 'threat.indicator.x509.public_key_exponent'?: string | number | undefined; 'threat.indicator.x509.public_key_size'?: string | number | undefined; 'threat.indicator.x509.serial_number'?: string | undefined; 'threat.indicator.x509.signature_algorithm'?: string | undefined; 'threat.indicator.x509.subject.common_name'?: string[] | undefined; 'threat.indicator.x509.subject.country'?: string[] | undefined; 'threat.indicator.x509.subject.distinguished_name'?: string | undefined; 'threat.indicator.x509.subject.locality'?: string[] | undefined; 'threat.indicator.x509.subject.organization'?: string[] | undefined; 'threat.indicator.x509.subject.organizational_unit'?: string[] | undefined; 'threat.indicator.x509.subject.state_or_province'?: string[] | undefined; 'threat.indicator.x509.version_number'?: string | undefined; 'threat.software.alias'?: string[] | undefined; 'threat.software.id'?: string | undefined; 'threat.software.name'?: string | undefined; 'threat.software.platforms'?: string[] | undefined; 'threat.software.reference'?: string | undefined; 'threat.software.type'?: string | undefined; 'threat.tactic.id'?: string[] | undefined; 'threat.tactic.name'?: string[] | undefined; 'threat.tactic.reference'?: string[] | undefined; 'threat.technique.id'?: string[] | undefined; 'threat.technique.name'?: string[] | undefined; 'threat.technique.reference'?: string[] | undefined; 'threat.technique.subtechnique.id'?: string[] | undefined; 'threat.technique.subtechnique.name'?: string[] | undefined; 'threat.technique.subtechnique.reference'?: string[] | undefined; 'tls.cipher'?: string | undefined; 'tls.client.certificate'?: string | undefined; 'tls.client.certificate_chain'?: string[] | undefined; 'tls.client.hash.md5'?: string | undefined; 'tls.client.hash.sha1'?: string | undefined; 'tls.client.hash.sha256'?: string | undefined; 'tls.client.issuer'?: string | undefined; 'tls.client.ja3'?: string | undefined; 'tls.client.not_after'?: string | number | undefined; 'tls.client.not_before'?: string | number | undefined; 'tls.client.server_name'?: string | undefined; 'tls.client.subject'?: string | undefined; 'tls.client.supported_ciphers'?: string[] | undefined; 'tls.client.x509.alternative_names'?: string[] | undefined; 'tls.client.x509.issuer.common_name'?: string[] | undefined; 'tls.client.x509.issuer.country'?: string[] | undefined; 'tls.client.x509.issuer.distinguished_name'?: string | undefined; 'tls.client.x509.issuer.locality'?: string[] | undefined; 'tls.client.x509.issuer.organization'?: string[] | undefined; 'tls.client.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.client.x509.issuer.state_or_province'?: string[] | undefined; 'tls.client.x509.not_after'?: string | number | undefined; 'tls.client.x509.not_before'?: string | number | undefined; 'tls.client.x509.public_key_algorithm'?: string | undefined; 'tls.client.x509.public_key_curve'?: string | undefined; 'tls.client.x509.public_key_exponent'?: string | number | undefined; 'tls.client.x509.public_key_size'?: string | number | undefined; 'tls.client.x509.serial_number'?: string | undefined; 'tls.client.x509.signature_algorithm'?: string | undefined; 'tls.client.x509.subject.common_name'?: string[] | undefined; 'tls.client.x509.subject.country'?: string[] | undefined; 'tls.client.x509.subject.distinguished_name'?: string | undefined; 'tls.client.x509.subject.locality'?: string[] | undefined; 'tls.client.x509.subject.organization'?: string[] | undefined; 'tls.client.x509.subject.organizational_unit'?: string[] | undefined; 'tls.client.x509.subject.state_or_province'?: string[] | undefined; 'tls.client.x509.version_number'?: string | undefined; 'tls.curve'?: string | undefined; 'tls.established'?: boolean | undefined; 'tls.next_protocol'?: string | undefined; 'tls.resumed'?: boolean | undefined; 'tls.server.certificate'?: string | undefined; 'tls.server.certificate_chain'?: string[] | undefined; 'tls.server.hash.md5'?: string | undefined; 'tls.server.hash.sha1'?: string | undefined; 'tls.server.hash.sha256'?: string | undefined; 'tls.server.issuer'?: string | undefined; 'tls.server.ja3s'?: string | undefined; 'tls.server.not_after'?: string | number | undefined; 'tls.server.not_before'?: string | number | undefined; 'tls.server.subject'?: string | undefined; 'tls.server.x509.alternative_names'?: string[] | undefined; 'tls.server.x509.issuer.common_name'?: string[] | undefined; 'tls.server.x509.issuer.country'?: string[] | undefined; 'tls.server.x509.issuer.distinguished_name'?: string | undefined; 'tls.server.x509.issuer.locality'?: string[] | undefined; 'tls.server.x509.issuer.organization'?: string[] | undefined; 'tls.server.x509.issuer.organizational_unit'?: string[] | undefined; 'tls.server.x509.issuer.state_or_province'?: string[] | undefined; 'tls.server.x509.not_after'?: string | number | undefined; 'tls.server.x509.not_before'?: string | number | undefined; 'tls.server.x509.public_key_algorithm'?: string | undefined; 'tls.server.x509.public_key_curve'?: string | undefined; 'tls.server.x509.public_key_exponent'?: string | number | undefined; 'tls.server.x509.public_key_size'?: string | number | undefined; 'tls.server.x509.serial_number'?: string | undefined; 'tls.server.x509.signature_algorithm'?: string | undefined; 'tls.server.x509.subject.common_name'?: string[] | undefined; 'tls.server.x509.subject.country'?: string[] | undefined; 'tls.server.x509.subject.distinguished_name'?: string | undefined; 'tls.server.x509.subject.locality'?: string[] | undefined; 'tls.server.x509.subject.organization'?: string[] | undefined; 'tls.server.x509.subject.organizational_unit'?: string[] | undefined; 'tls.server.x509.subject.state_or_province'?: string[] | undefined; 'tls.server.x509.version_number'?: string | undefined; 'tls.version'?: string | undefined; 'tls.version_protocol'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.id'?: string | undefined; 'url.domain'?: string | undefined; 'url.extension'?: string | undefined; 'url.fragment'?: string | undefined; 'url.full'?: string | undefined; 'url.original'?: string | undefined; 'url.password'?: string | undefined; 'url.path'?: string | undefined; 'url.port'?: string | number | undefined; 'url.query'?: string | undefined; 'url.registered_domain'?: string | undefined; 'url.scheme'?: string | undefined; 'url.subdomain'?: string | undefined; 'url.top_level_domain'?: string | undefined; 'url.username'?: string | undefined; 'user.changes.domain'?: string | undefined; 'user.changes.email'?: string | undefined; 'user.changes.full_name'?: string | undefined; 'user.changes.group.domain'?: string | undefined; 'user.changes.group.id'?: string | undefined; 'user.changes.group.name'?: string | undefined; 'user.changes.hash'?: string | undefined; 'user.changes.id'?: string | undefined; 'user.changes.name'?: string | undefined; 'user.changes.roles'?: string[] | undefined; 'user.domain'?: string | undefined; 'user.effective.domain'?: string | undefined; 'user.effective.email'?: string | undefined; 'user.effective.full_name'?: string | undefined; 'user.effective.group.domain'?: string | undefined; 'user.effective.group.id'?: string | undefined; 'user.effective.group.name'?: string | undefined; 'user.effective.hash'?: string | undefined; 'user.effective.id'?: string | undefined; 'user.effective.name'?: string | undefined; 'user.effective.roles'?: string[] | undefined; 'user.email'?: string | undefined; 'user.full_name'?: string | undefined; 'user.group.domain'?: string | undefined; 'user.group.id'?: string | undefined; 'user.group.name'?: string | undefined; 'user.hash'?: string | undefined; 'user.id'?: string | undefined; 'user.name'?: string | undefined; 'user.risk.calculated_level'?: string | undefined; 'user.risk.calculated_score'?: number | undefined; 'user.risk.calculated_score_norm'?: number | undefined; 'user.risk.static_level'?: string | undefined; 'user.risk.static_score'?: number | undefined; 'user.risk.static_score_norm'?: number | undefined; 'user.roles'?: string[] | undefined; 'user.target.domain'?: string | undefined; 'user.target.email'?: string | undefined; 'user.target.full_name'?: string | undefined; 'user.target.group.domain'?: string | undefined; 'user.target.group.id'?: string | undefined; 'user.target.group.name'?: string | undefined; 'user.target.hash'?: string | undefined; 'user.target.id'?: string | undefined; 'user.target.name'?: string | undefined; 'user.target.roles'?: string[] | undefined; 'user_agent.device.name'?: string | undefined; 'user_agent.name'?: string | undefined; 'user_agent.original'?: string | undefined; 'user_agent.os.family'?: string | undefined; 'user_agent.os.full'?: string | undefined; 'user_agent.os.kernel'?: string | undefined; 'user_agent.os.name'?: string | undefined; 'user_agent.os.platform'?: string | undefined; 'user_agent.os.type'?: string | undefined; 'user_agent.os.version'?: string | undefined; 'user_agent.version'?: string | undefined; 'vulnerability.category'?: string[] | undefined; 'vulnerability.classification'?: string | undefined; 'vulnerability.description'?: string | undefined; 'vulnerability.enumeration'?: string | undefined; 'vulnerability.id'?: string | undefined; 'vulnerability.reference'?: string | undefined; 'vulnerability.report_id'?: string | undefined; 'vulnerability.scanner.vendor'?: string | undefined; 'vulnerability.score.base'?: number | undefined; 'vulnerability.score.environmental'?: number | undefined; 'vulnerability.score.temporal'?: number | undefined; 'vulnerability.score.version'?: string | undefined; 'vulnerability.severity'?: string | undefined; } & {} & { 'ecs.version'?: string | undefined; 'kibana.alert.risk_score'?: number | undefined; 'kibana.alert.rule.author'?: string | undefined; 'kibana.alert.rule.created_at'?: string | number | 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 | number | 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 | number | undefined; 'kibana.alert.suppression.end'?: string | number | undefined; 'kibana.alert.suppression.start'?: string | number | undefined; 'kibana.alert.suppression.terms.field'?: 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; }" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts", "deprecated": false, @@ -429,7 +437,15 @@ "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.maintenance_window_ids\": { readonly type: \"keyword\"; 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.revision\": { readonly type: \"long\"; 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.url\": { readonly type: \"keyword\"; readonly array: false; readonly index: false; readonly required: false; readonly ignore_above: 2048; }; 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.alert.workflow_tags\": { readonly type: \"keyword\"; readonly array: true; 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 \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; readonly \"@timestamp\": { readonly type: \"date\"; readonly required: true; readonly array: false; }; readonly \"kibana.version\": { readonly type: \"version\"; readonly array: false; readonly required: false; }; }" + "{ 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.maintenance_window_ids\": { readonly type: \"keyword\"; 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 multi_fields: ", + { + "pluginId": "@kbn/alerts-as-data-utils", + "scope": "common", + "docId": "kibKbnAlertsAsDataUtilsPluginApi", + "section": "def-common.MultiField", + "text": "MultiField" + }, + "[]; }; 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.revision\": { readonly type: \"long\"; 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.url\": { readonly type: \"keyword\"; readonly array: false; readonly index: false; readonly required: false; readonly ignore_above: 2048; }; 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.alert.workflow_tags\": { readonly type: \"keyword\"; readonly array: true; 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 \"kibana.space_ids\": { readonly type: \"keyword\"; readonly array: true; readonly required: true; }; readonly tags: { readonly type: \"keyword\"; readonly array: true; readonly required: false; }; 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, diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index d55d480f98b29..5b017db7bac95 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 79c3354c1b347..296e017b07595 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-11-22 +date: 2023-11-30 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 d5a66bd531dc6..51acb112dd9cf 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 2fd0a65a4ff22..d695962d56d12 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -718,14 +718,6 @@ "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/analytics/analytics_service.ts" }, - { - "plugin": "@kbn/subscription-tracking", - "path": "packages/kbn-subscription-tracking/src/use_go_to_subscription.ts" - }, - { - "plugin": "@kbn/subscription-tracking", - "path": "packages/kbn-subscription-tracking/src/use_impression.ts" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts" @@ -796,15 +788,15 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts" }, { "plugin": "securitySolution", @@ -872,19 +864,19 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, { "plugin": "@kbn/core-analytics-browser-mocks", diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 54dfd05830ebb..1033ee3d89053 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 949091883e1f9..5a5034563ee84 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 7a3fa1ef4dfa9..7a359de1e2c77 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-11-22 +date: 2023-11-30 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 a7479123c3375..838e043df0069 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-11-22 +date: 2023-11-30 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 6f3483a0b6322..48b39e1d90242 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-11-22 +date: 2023-11-30 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 6f555620c6f4f..3cc052bc1d843 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-11-22 +date: 2023-11-30 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 8d8bc2f8a3a69..7389fba61e6bb 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-11-22 +date: 2023-11-30 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 0c1c80a73ba9b..605b31344c1fe 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-11-22 +date: 2023-11-30 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 8953dd760806b..3aa5c4acf9e8e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 5b6ff91758d33..c84fecba9bc20 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-11-22 +date: 2023-11-30 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 701e473e2d0fa..65edd82ebe63d 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-11-22 +date: 2023-11-30 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 b569972976b65..b8259d55e9070 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 5454da1e6f51d..dd64eccf7819a 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.devdocs.json b/api_docs/kbn_calculate_width_from_char_count.devdocs.json new file mode 100644 index 0000000000000..fb146897c3e53 --- /dev/null +++ b/api_docs/kbn_calculate_width_from_char_count.devdocs.json @@ -0,0 +1,147 @@ +{ + "id": "@kbn/calculate-width-from-char-count", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromCharCount", + "type": "Function", + "tags": [], + "label": "calculateWidthFromCharCount", + "description": [], + "signature": [ + "(labelLength: number, overridesPanelWidths: Partial<", + "LIMITS", + "> | undefined) => number" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromCharCount.$1", + "type": "number", + "tags": [], + "label": "labelLength", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromCharCount.$2", + "type": "Object", + "tags": [], + "label": "overridesPanelWidths", + "description": [], + "signature": [ + "Partial<", + "LIMITS", + "> | undefined" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromEntries", + "type": "Function", + "tags": [], + "label": "calculateWidthFromEntries", + "description": [], + "signature": [ + "(entries: string[] | Record[], labelKeys: string[] | undefined, overridesPanelWidths: Partial<", + "LIMITS", + "> | undefined) => number" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromEntries.$1", + "type": "CompoundType", + "tags": [], + "label": "entries", + "description": [], + "signature": [ + "string[] | Record[]" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromEntries.$2", + "type": "Array", + "tags": [], + "label": "labelKeys", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/calculate-width-from-char-count", + "id": "def-common.calculateWidthFromEntries.$3", + "type": "Object", + "tags": [], + "label": "overridesPanelWidths", + "description": [], + "signature": [ + "Partial<", + "LIMITS", + "> | undefined" + ], + "path": "packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx new file mode 100644 index 0000000000000..937284372211f --- /dev/null +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCalculateWidthFromCharCountPluginApi +slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count +title: "@kbn/calculate-width-from-char-count" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/calculate-width-from-char-count plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] +--- +import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; + + + +Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 7 | 0 | 7 | 1 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 11153ea396c3d..46745458f4d20 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-11-22 +date: 2023-11-30 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 fe27f39e67fdb..e275c63dda2c9 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-11-22 +date: 2023-11-30 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 4922e4730ca73..5bbb5f0aa435f 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-11-22 +date: 2023-11-30 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 a0b5f530e3b2d..6e927b0979253 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-11-22 +date: 2023-11-30 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 085ba245f1d41..ca606c1bcdf72 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-11-22 +date: 2023-11-30 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 2669569a7d4d5..0c95285d554ed 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-11-22 +date: 2023-11-30 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 f556c159854f8..23c39200b6296 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-11-22 +date: 2023-11-30 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 11793c6723381..0d1099bc37f77 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-11-22 +date: 2023-11-30 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 e625c9daaa83d..32008ffdc3426 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 043b3241019f4..5484c8e7d2abb 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-11-22 +date: 2023-11-30 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 97c88e5f84960..bb353457cdbd5 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.devdocs.json b/api_docs/kbn_config_mocks.devdocs.json index 29e17d6500043..c470bc5513b23 100644 --- a/api_docs/kbn_config_mocks.devdocs.json +++ b/api_docs/kbn_config_mocks.devdocs.json @@ -310,7 +310,7 @@ }, "[]][], [], unknown>; atPath: jest.MockInstance<", "Observable", - ", [path: ", + ", [", { "pluginId": "@kbn/config", "scope": "common", @@ -318,7 +318,7 @@ "section": "def-common.ConfigPath", "text": "ConfigPath" }, - "], unknown>; atPathSync: jest.MockInstance; atPathSync: jest.MockInstance; }> | undefined>; attributes: ", + "; statusCode: number; }> | undefined>; attributes: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4021,7 +4021,7 @@ "section": "def-common.Type", "text": "Type" }, - "; }> | undefined; namespaces?: string[] | undefined; createdAt?: string | undefined; updatedAt?: string | undefined; version?: string | undefined; originId?: string | undefined; } & { type: string; id: string; references: Readonly<{ name?: string | undefined; } & { type: string; id: string; }>[]; attributes: Readonly<{ [x: string]: any; } & {}>; }> | undefined>" + "; statusCode: number; }> | undefined; namespaces?: string[] | undefined; createdAt?: string | undefined; updatedAt?: string | undefined; version?: string | undefined; originId?: string | undefined; } & { type: string; id: string; references: Readonly<{ name?: string | undefined; } & { type: string; id: string; }>[]; attributes: Readonly<{ [x: string]: any; } & {}>; }> | undefined>" ], "path": "packages/kbn-content-management-utils/src/schema.ts", "deprecated": false, diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 40a21ceb52f4d..0025128a2fb71 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 3c5485970e2f1..fd77c783499df 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-11-22 +date: 2023-11-30 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 6529576315313..5913d0ceac884 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-11-22 +date: 2023-11-30 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 a19c0bc42463c..d5c3b1004a05a 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-11-22 +date: 2023-11-30 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 7e50f98092c38..05da627bc9ba7 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-11-22 +date: 2023-11-30 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 fdff30f9a8cfb..e7ed5fbae36fb 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-11-22 +date: 2023-11-30 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 26da43962c0c1..cfe499e7b00d6 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-11-22 +date: 2023-11-30 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 8de9f1b310fe2..fbf420bfa3f58 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-11-22 +date: 2023-11-30 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 6db8727e603cf..95f857ccb6e98 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-11-22 +date: 2023-11-30 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 58d17eaccaacc..3c15e355b4b37 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-11-22 +date: 2023-11-30 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 51e56c214969d..9a13b1a3a99b9 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-11-22 +date: 2023-11-30 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 be6a79097edcf..c2bef93d1f3f5 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-11-22 +date: 2023-11-30 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 6e3df5e71cca1..fbc5a26540942 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-11-22 +date: 2023-11-30 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 a0fa88b6f48ed..6a5ff364e6063 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-11-22 +date: 2023-11-30 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 5ec97a5f94069..5ab42ff2bc81e 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-11-22 +date: 2023-11-30 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 c4cdddc5f0c13..2d54a1f0b6c1d 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-11-22 +date: 2023-11-30 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 9d4dff4490193..f312e32f8029c 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-11-22 +date: 2023-11-30 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 6cc70f6388930..2f8820e35c7f1 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-11-22 +date: 2023-11-30 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 9c1e5ca77f390..262ae1955ee1c 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-11-22 +date: 2023-11-30 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 74b35529235e8..64e717febd145 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-11-22 +date: 2023-11-30 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 5733a313252f8..493cc13f487c3 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-11-22 +date: 2023-11-30 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 fec176fc41f33..33ae90c72bbef 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index 26bfd6ef40f09..84be089b48632 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -1596,15 +1596,12 @@ { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeProjectNavigationNode.path", - "type": "Array", + "type": "string", "tags": [], "label": "path", "description": [ "Path in the tree of the node" ], - "signature": [ - "string[]" - ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, "trackAdoption": false @@ -1657,12 +1654,30 @@ }, { "parentPluginId": "@kbn/core-chrome-browser", - "id": "def-common.ChromeProjectNavigationNode.isActive", + "id": "def-common.ChromeProjectNavigationNode.renderItem", + "type": "Function", + "tags": [], + "label": "renderItem", + "description": [ + "\nHandler to render the node item with custom JSX. This handler is added to render the `children` of\nthe Navigation.Item component when React components are used to declare the navigation tree." + ], + "signature": [ + "(() => React.ReactNode) | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.isElasticInternalLink", "type": "CompoundType", "tags": [], - "label": "isActive", + "label": "isElasticInternalLink", "description": [ - "\nFlag to indicate if the node is currently active." + "\nFlag to indicate if the node is an \"external\" cloud link" ], "signature": [ "boolean | undefined" @@ -3226,7 +3241,7 @@ "label": "EuiThemeSize", "description": [], "signature": [ - "\"m\" | \"s\" | \"xs\" | \"l\" | \"xl\" | \"xxl\"" + "\"m\" | \"s\" | \"l\" | \"xs\" | \"xl\" | \"xxl\"" ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index c0a19d7e48fa1..316c19d217149 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 165 | 0 | 70 | 0 | +| 166 | 0 | 70 | 0 | ## Common diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index e4fe110ebb92a..156d4dd5740db 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-11-22 +date: 2023-11-30 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 5c848bb67a726..d3f3f05fdb6f5 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-11-22 +date: 2023-11-30 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 46e677e8af5dc..9e510ca0c5c72 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-11-22 +date: 2023-11-30 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 490e0a8698f9c..1fd77880a135d 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-11-22 +date: 2023-11-30 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 00219dc66e86b..a9996954d1dd8 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-11-22 +date: 2023-11-30 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 fd606cb483a6a..78b3f63fd491a 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-11-22 +date: 2023-11-30 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 4f1d2dad1f395..de78e4c889cc4 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-11-22 +date: 2023-11-30 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 81510a572786f..1068470504b3b 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-11-22 +date: 2023-11-30 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 79ebe8a60b7ad..daa23595e61e8 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-11-22 +date: 2023-11-30 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 3d8e8f62c9c6f..d94d5a2f90afa 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-11-22 +date: 2023-11-30 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 89d40489e5e0d..c92afb612e907 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-11-22 +date: 2023-11-30 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 dacbfdf60bd6d..7959bd19f7d41 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-11-22 +date: 2023-11-30 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 61988aa746e5e..7d6cb43d509f2 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-11-22 +date: 2023-11-30 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 9533d0dadcf71..1ae964ab2afc1 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-11-22 +date: 2023-11-30 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 c11ea33512f28..4956dc44fc716 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-11-22 +date: 2023-11-30 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 b83b9943ec1ee..1b4f0a33316c9 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-11-22 +date: 2023-11-30 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 21388e6f4ff7d..cc4a5ec79f326 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-11-22 +date: 2023-11-30 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 01350d344b511..1b5be4dd4ee66 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-11-22 +date: 2023-11-30 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 700956006d2f8..66473b0f70fd6 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-11-22 +date: 2023-11-30 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 0fe32e5e72455..98960fe94d8f0 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-11-22 +date: 2023-11-30 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 7aad109ee0a26..f01b4cf1b858c 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-11-22 +date: 2023-11-30 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 af26cbd7aa10b..933882793dd57 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-11-22 +date: 2023-11-30 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 039d9fd72f2a1..8d83caf6ed83b 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-11-22 +date: 2023-11-30 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 f6124a8faff6e..48bfc5390b611 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-11-22 +date: 2023-11-30 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 2fb0e355314b7..c0f8322de528b 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-11-22 +date: 2023-11-30 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 8cc9125aaca53..5304cbf7a4ff6 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-11-22 +date: 2023-11-30 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 eeef4dc108282..7f8b429bf80d7 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-11-22 +date: 2023-11-30 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 92949c3f77005..c6a8478777393 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-11-22 +date: 2023-11-30 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 0cc9743c77d63..ace0c742419a3 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-11-22 +date: 2023-11-30 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 8950bb264fc33..1e16c1e93fd14 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-11-22 +date: 2023-11-30 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 1484dc2720da9..784271a07e1d9 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-11-22 +date: 2023-11-30 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 fa7fbaecd2a1c..08de9500ac660 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-11-22 +date: 2023-11-30 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 5423a1fdc8ed8..8b1ee90f6e2e9 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-11-22 +date: 2023-11-30 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 835c756ba3ee4..ecd79ee1acf08 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-11-22 +date: 2023-11-30 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 56cbc8236e44f..2d787309d6525 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-11-22 +date: 2023-11-30 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 d40b3f96e12ec..99eb3713657a5 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-11-22 +date: 2023-11-30 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 d0027996968d7..97c330ff643fd 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-11-22 +date: 2023-11-30 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 9b490f7354e49..d6c8f675d51e5 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-11-22 +date: 2023-11-30 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 f16f9773b3a58..78fe8f2170898 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-11-22 +date: 2023-11-30 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 173a72aa17ea1..8862ae99d5d79 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-11-22 +date: 2023-11-30 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 51b2613c51891..82d168ff2791b 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-11-22 +date: 2023-11-30 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 5a64d2aa15ce3..5f2161e825cf7 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 088445213eb42..870a963e08a7d 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-11-22 +date: 2023-11-30 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 5fe9d319b2336..5a5021f6fea2a 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 97725e587467d..7f35207ea3ba1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 26aa00d245ab8..7887cb4805b84 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 18fcb3262c963..a459d76ca6828 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-11-22 +date: 2023-11-30 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 2fe742baa1d7b..9dd44697fe241 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3732,6 +3732,10 @@ "plugin": "assetManager", "path": "x-pack/plugins/asset_manager/server/routes/assets/containers.ts" }, + { + "plugin": "assetManager", + "path": "x-pack/plugins/asset_manager/server/routes/assets/pods.ts" + }, { "plugin": "banners", "path": "x-pack/plugins/banners/server/routes/info.ts" @@ -4040,6 +4044,10 @@ "plugin": "enterpriseSearch", "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts" }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts" + }, { "plugin": "enterpriseSearch", "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/mapping.ts" @@ -13639,7 +13647,15 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_status_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts" }, { "plugin": "securitySolution", @@ -14897,27 +14913,27 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_init_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.ts" + "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts" }, { "plugin": "securitySolution", diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index bed7c32785468..b6eb0fe24c2b6 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.devdocs.json b/api_docs/kbn_core_http_server_internal.devdocs.json index 73de455f5bffe..777bf0f988ab2 100644 --- a/api_docs/kbn_core_http_server_internal.devdocs.json +++ b/api_docs/kbn_core_http_server_internal.devdocs.json @@ -693,17 +693,9 @@ "label": "setup", "description": [], "signature": [ - "(config: ", - { - "pluginId": "@kbn/core-http-server-internal", - "scope": "common", - "docId": "kibKbnCoreHttpServerInternalPluginApi", - "section": "def-common.HttpConfig", - "text": "HttpConfig" - }, - ", executionContext?: ", - "IExecutionContext", - " | undefined) => Promise<", + "({ config$, executionContext, }: ", + "HttpServerSetupOptions", + ") => Promise<", "HttpServerSetup", ">" ], @@ -716,37 +708,15 @@ "id": "def-common.HttpServer.setup.$1", "type": "Object", "tags": [], - "label": "config", + "label": "{\n config$,\n executionContext,\n }", "description": [], "signature": [ - { - "pluginId": "@kbn/core-http-server-internal", - "scope": "common", - "docId": "kibKbnCoreHttpServerInternalPluginApi", - "section": "def-common.HttpConfig", - "text": "HttpConfig" - } + "HttpServerSetupOptions" ], "path": "packages/core/http/core-http-server-internal/src/http_server.ts", "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "@kbn/core-http-server-internal", - "id": "def-common.HttpServer.setup.$2", - "type": "Object", - "tags": [], - "label": "executionContext", - "description": [], - "signature": [ - "IExecutionContext", - " | undefined" - ], - "path": "packages/core/http/core-http-server-internal/src/http_server.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false } ], "returnComment": [] diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index bf617de63f714..11decde852e01 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_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 | |-------------------|-----------|------------------------|-----------------| -| 59 | 0 | 53 | 8 | +| 58 | 0 | 52 | 9 | ## Common diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index ea78f5773a544..9fd169f4050e1 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-11-22 +date: 2023-11-30 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 aa41aea93b84d..d82cc5a4455de 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-11-22 +date: 2023-11-30 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 dc17733864d75..5fdb9ab1b017b 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-11-22 +date: 2023-11-30 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 9b5853c1de51f..590b532fdd289 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-11-22 +date: 2023-11-30 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 a4d579b051867..1beb63f3e586e 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-11-22 +date: 2023-11-30 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 05dce9a5481b9..b9fd0f085f42a 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-11-22 +date: 2023-11-30 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 91e21af1911ad..4ed6860b53223 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-11-22 +date: 2023-11-30 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 bb4d1e041d316..c0c8d3605225f 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-11-22 +date: 2023-11-30 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 4e1dd47b1e985..d63e6c38298c3 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-11-22 +date: 2023-11-30 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 7c3c8f4680556..ec79d1e75adf0 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-11-22 +date: 2023-11-30 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 a3b5103e9b60e..e92e325c7c126 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-11-22 +date: 2023-11-30 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 5b425d5135a50..aec5407e06805 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-11-22 +date: 2023-11-30 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 943bd21c87c89..179401d51bd02 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-11-22 +date: 2023-11-30 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 5b8af676c2632..a1f38d78aa106 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-11-22 +date: 2023-11-30 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 1debc6e4c518e..52709de895f51 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 86c9c38ab7ca2..9498b18e2bba2 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index af82e58e7b25a..707a3348452b3 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-11-22 +date: 2023-11-30 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 8eb823e88ef70..faa59dfee3528 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-11-22 +date: 2023-11-30 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 d88502cb0bdf4..52fa16455f0d0 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-11-22 +date: 2023-11-30 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 944c3da9c6c75..ee533128d7eac 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-11-22 +date: 2023-11-30 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 e9b446468cfaa..192e02612f6c9 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-11-22 +date: 2023-11-30 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 7121b52619481..3e44eb52ac0e2 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-11-22 +date: 2023-11-30 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 fdf53b95afa28..9fe08c2c9f318 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-11-22 +date: 2023-11-30 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 1760e14a33e02..2b7e828396289 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index e57e44669a179..9713e7142176c 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 5e8a5c50baffd..a394db59653f6 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index f542ce3665d4c..e65611376db50 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-11-22 +date: 2023-11-30 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 883debb2cb3aa..c28c518e2ade3 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-11-22 +date: 2023-11-30 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 35493c375e6a0..76c24d8d8140d 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-11-22 +date: 2023-11-30 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 d1bd3b0205c0e..f85a294166d0e 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-11-22 +date: 2023-11-30 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 15850b3b56a56..91ba5fbb3c59c 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-11-22 +date: 2023-11-30 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 b69533a26612f..daf7a1a4a66fa 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-11-22 +date: 2023-11-30 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 71a93f95978ff..774f857a16301 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-11-22 +date: 2023-11-30 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 096ae6bcab94d..35e68259281dd 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-11-22 +date: 2023-11-30 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 e8acd06228a91..ceb56808dcb59 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index 0c826062ac20b..7fc2a392370ba 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 29d81e7a42bde..49da04677f9a3 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 092c00d186ab5..405471b44455f 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-11-22 +date: 2023-11-30 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 6221b322656cf..c428ae43e7ea2 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-11-22 +date: 2023-11-30 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 f25d9f091788f..c9b2e1679c041 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-11-22 +date: 2023-11-30 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 a4473b6241b8f..bfcd315fc8954 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-11-22 +date: 2023-11-30 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 890ca25de80b0..943e8a1c780a6 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-11-22 +date: 2023-11-30 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 ea2998b74f345..f8ff4563959fb 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-11-22 +date: 2023-11-30 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 e3854d950c3d8..70c48fe2acf2a 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index ffeb131e6fc08..38fa6388ca84c 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index f5b3e4c68a760..d6801271c14b5 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index 143bed79f4d08..51dfc7d588784 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -3901,6 +3901,22 @@ "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectsBulkUpdateOptions.migrationVersionCompatibility", + "type": "CompoundType", + "tags": [], + "label": "migrationVersionCompatibility", + "description": [ + "{@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility}" + ], + "signature": [ + "\"raw\" | \"compatible\" | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -5063,7 +5079,7 @@ "tags": [], "label": "bulkUpdate", "description": [ - "\nBulk Updates multiple SavedObject at once\n" + "\nBulk Updates multiple SavedObject at once\n\nThe savedObjects `bulkUpdate` API will update documents client-side and then reindex the updated documents.\nThese update operations are done in-memory, and cause memory constraint issues when\nupdating many objects with large `json` blobs stored in some fields. As such, we recommend against using\n`bulkUpdate` for savedObjects that:\n- use arrays (as these tend to be large objects)\n- store large `json` blobs in some fields\n" ], "signature": [ "(objects: ", diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index ae5d2fa0f07e4..658177adebb58 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_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 | |-------------------|-----------|------------------------|-----------------| -| 352 | 1 | 5 | 2 | +| 353 | 1 | 5 | 2 | ## Common 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 3e286e34b604d..1c24cca7ea9f1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 62ab342d828d6..196cb5f47138b 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-11-22 +date: 2023-11-30 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 67d1383602783..19b86390bb057 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-11-22 +date: 2023-11-30 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 2dcf40180816a..65c2b66236398 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-11-22 +date: 2023-11-30 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 83f991ea7b0a0..cf8f97220f7c3 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-11-22 +date: 2023-11-30 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 f90a3d01e7fd7..528cdf99bf2c8 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 18023feba7929..560b7ffb9413f 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-11-22 +date: 2023-11-30 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 85b39450f26f7..a93e665b668e7 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-11-22 +date: 2023-11-30 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 74fc90247614c..ae316087c45b3 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index afbaf9ced9592..aca9b4e650bcb 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 6d09ac52f50cc..4c331a7b25f56 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-11-22 +date: 2023-11-30 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 3228ea065ed26..b04287ece28dc 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-11-22 +date: 2023-11-30 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 00c2d570450a6..4019d3550bda0 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-11-22 +date: 2023-11-30 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 f433817a2ec61..0df6190b445b6 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-11-22 +date: 2023-11-30 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 c0224b85edb70..8e72ffea83ac0 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-11-22 +date: 2023-11-30 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 d0e46a3d14a92..8f63af040222c 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-11-22 +date: 2023-11-30 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 d85f8d1454f37..daf9b0725da59 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-11-22 +date: 2023-11-30 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 5a8a7a9424cfe..24bd9139388ef 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-11-22 +date: 2023-11-30 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 233c9413f3807..2f2cc86c123e2 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-11-22 +date: 2023-11-30 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 1edc829cdec0f..8fd3ef3d400f7 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-11-22 +date: 2023-11-30 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 54a20a31a5d6f..235ad6bccb286 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-11-22 +date: 2023-11-30 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 dd0f53a93e85a..3bbd0c1a8494a 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-11-22 +date: 2023-11-30 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 cb8c14b7662ce..a0cfc53b59cd3 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index c99940435e8ca..bebe63743796a 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 46f7c9d03afd5..615b72021f677 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-11-22 +date: 2023-11-30 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 0b152f10682b9..e4d6b7039b524 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-11-22 +date: 2023-11-30 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 4d56169048712..50f2d5bbbfaa7 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 4cca2c99b38b5..807f84e835f67 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-11-22 +date: 2023-11-30 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 b1d4bfb578c86..b31742792a185 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-11-22 +date: 2023-11-30 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 12362e7ca00c3..6f9456f9d0811 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-11-22 +date: 2023-11-30 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 51c6a0373ebd7..72c4f15f5dfd3 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-11-22 +date: 2023-11-30 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 bab3a50b04a50..3a29453af2bcb 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-11-22 +date: 2023-11-30 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 068d6e2d45f1f..e48aad8225859 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-11-22 +date: 2023-11-30 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 3d93ed2c12438..8ee5fd6fb1e63 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-11-22 +date: 2023-11-30 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 6712c34766b52..cae0028dd414e 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-11-22 +date: 2023-11-30 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 8d8500d22202c..e85cede9f22a1 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-11-22 +date: 2023-11-30 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 d3449936a65a2..61c041450589f 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-11-22 +date: 2023-11-30 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 0ccc4ff304177..4a15daa6dc964 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 3d3442b3d112c..8d33710cc9445 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index 96b3f724e1315..e30818e2e532e 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 9878f33c836bb..a94d85c11414f 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 46beb586fe0c7..06c0c6557bb7e 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-11-22 +date: 2023-11-30 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 87fcf87379978..327f4a1453f7c 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 67288c39a6d6c..289f04197a848 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index c9b9b3d2ebf89..917d2e2d3b118 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index b7200d3a8ca1f..3f3764304e644 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 0c01dcfcf7f09..3884f08923a99 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index a26dc4909b44b..c20d177d4b1c1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 83a59b840c03c..3b56f6adca9f0 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 827b5b14e6133..d9e9d8d1eb42d 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 83a7469c80c80..44e2e8fcd1930 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 52ee4756bd94f..9198f06621712 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 1b86219e682c8..52036d59a1007 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 26fe6128d0e60..060f909f83038 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 0d264422db2d8..9778918e63b41 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 0e30e8df2aea9..a00dd318fb717 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index f45413ff73629..36cea36753dcb 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 5b98c3bc0a0ec..80bc8af24e661 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 7bc1d3c191ba7..d226d9dea421c 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-11-22 +date: 2023-11-30 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 3eba5d0d0fc9a..3ab2a0209bb58 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-11-22 +date: 2023-11-30 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 ed87775239e1f..680deb5ddc9e8 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-11-22 +date: 2023-11-30 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 e30bf1d855601..bb52a9c342d49 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index 98fec7a1f0134..2380078d42c51 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index d79272c54bcfa..a0744a180ea15 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -300,7 +300,7 @@ "label": "enterpriseSearch", "description": [], "signature": [ - "{ readonly aiSearchDoc: string; readonly aiSearchHelp: string; readonly apiKeys: string; readonly behavioralAnalytics: string; readonly behavioralAnalyticsCORS: string; readonly behavioralAnalyticsEvents: string; readonly buildConnector: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsAzureBlobStorage: string; readonly connectorsBox: string; readonly connectorsClients: string; readonly connectorsConfluence: string; readonly connectorsContentExtraction: string; readonly connectorsDropbox: string; readonly connectorsGithub: string; readonly connectorsGoogleCloudStorage: string; readonly connectorsGoogleDrive: string; readonly connectorsGmail: string; readonly connectorsJira: string; readonly connectorsMicrosoftSQL: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsNative: string; readonly connectorsNetworkDrive: string; readonly connectorsOneDrive: string; readonly connectorsOracle: string; readonly connectorsOutlook: string; readonly connectorsPostgreSQL: string; readonly connectorsS3: string; readonly connectorsSalesforce: string; readonly connectorsServiceNow: string; readonly connectorsSharepoint: string; readonly connectorsSharepointOnline: string; readonly connectorsTeams: string; readonly connectorsSlack: string; readonly connectorsZoom: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly elser: string; readonly engines: string; readonly indexApi: string; readonly ingestionApis: string; readonly ingestPipelines: string; readonly knnSearch: string; readonly knnSearchCombine: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly machineLearningStart: string; readonly mailService: string; readonly mlDocumentEnrichment: string; readonly mlDocumentEnrichmentUpdateMappings: string; readonly searchApplicationsTemplates: string; readonly searchApplicationsSearchApi: string; readonly searchApplications: string; readonly searchApplicationsSearch: string; readonly searchLabs: string; readonly searchLabsRepo: string; readonly searchTemplates: string; readonly start: string; readonly supportedNlpModels: string; readonly syncRules: string; readonly trainedModels: string; readonly textEmbedding: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" + "{ readonly aiSearchDoc: string; readonly aiSearchHelp: string; readonly apiKeys: string; readonly behavioralAnalytics: string; readonly behavioralAnalyticsCORS: string; readonly behavioralAnalyticsEvents: string; readonly buildConnector: string; readonly bulkApi: string; readonly configuration: string; readonly connectors: string; readonly connectorsAzureBlobStorage: string; readonly connectorsBox: string; readonly connectorsClients: string; readonly connectorsConfluence: string; readonly connectorsContentExtraction: string; readonly connectorsDropbox: string; readonly connectorsGithub: string; readonly connectorsGoogleCloudStorage: string; readonly connectorsGoogleDrive: string; readonly connectorsGmail: string; readonly connectorsJira: string; readonly connectorsMicrosoftSQL: string; readonly connectorsMongoDB: string; readonly connectorsMySQL: string; readonly connectorsNative: string; readonly connectorsNetworkDrive: string; readonly connectorsOneDrive: string; readonly connectorsOracle: string; readonly connectorsOutlook: string; readonly connectorsPostgreSQL: string; readonly connectorsS3: string; readonly connectorsSalesforce: string; readonly connectorsServiceNow: string; readonly connectorsSharepoint: string; readonly connectorsSharepointOnline: string; readonly connectorsTeams: string; readonly connectorsSlack: string; readonly connectorsZoom: string; readonly crawlerExtractionRules: string; readonly crawlerManaging: string; readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; readonly elser: string; readonly engines: string; readonly indexApi: string; readonly ingestionApis: string; readonly ingestPipelines: string; readonly knnSearch: string; readonly knnSearchCombine: string; readonly languageAnalyzers: string; readonly languageClients: string; readonly licenseManagement: string; readonly machineLearningStart: string; readonly mailService: string; readonly mlDocumentEnrichment: string; readonly searchApplicationsTemplates: string; readonly searchApplicationsSearchApi: string; readonly searchApplications: string; readonly searchApplicationsSearch: string; readonly searchLabs: string; readonly searchLabsRepo: string; readonly searchTemplates: string; readonly start: string; readonly supportedNlpModels: string; readonly syncRules: string; readonly trainedModels: string; readonly textEmbedding: string; readonly troubleshootSetup: string; readonly usersAccess: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -546,7 +546,7 @@ "label": "securitySolution", "description": [], "signature": [ - "{ readonly artifactControl: string; readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; readonly configureEndpointIntegrationPolicy: string; readonly exceptions: { value_lists: string; }; readonly privileges: string; readonly manageDetectionRules: string; readonly createEsqlRuleType: string; }" + "{ readonly artifactControl: string; readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly endpointArtifacts: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; linux_deadlock: string; }; readonly packageActionTroubleshooting: { es_connection: string; }; readonly threatIntelInt: string; readonly responseActions: string; readonly configureEndpointIntegrationPolicy: string; readonly exceptions: { value_lists: string; }; readonly privileges: string; readonly manageDetectionRules: string; readonly createEsqlRuleType: string; readonly entityAnalytics: { readonly riskScorePrerequisites: string; }; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -840,7 +840,7 @@ "label": "fleet", "description": [], "signature": [ - "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly esSettings: string; readonly settings: string; readonly logstashSettings: string; readonly kafkaSettings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly datastreamsManualRollover: string; readonly datastreamsTSDS: string; readonly datastreamsTSDSMetrics: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; readonly api: string; readonly uninstallAgent: string; readonly installAndUninstallIntegrationAssets: string; readonly elasticAgentInputConfiguration: string; readonly policySecrets: string; }" + "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly esSettings: string; readonly settings: string; readonly logstashSettings: string; readonly kafkaSettings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly datastreamsManualRollover: string; readonly datastreamsTSDS: string; readonly datastreamsTSDSMetrics: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; readonly api: string; readonly uninstallAgent: string; readonly installAndUninstallIntegrationAssets: string; readonly elasticAgentInputConfiguration: string; readonly policySecrets: string; readonly remoteESOoutput: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -868,7 +868,7 @@ "label": "clients", "description": [], "signature": [ - "{ readonly guide: string; readonly goConnecting: string; readonly goGettingStarted: string; readonly goIndex: string; readonly goOverview: string; readonly javaBasicAuthentication: string; readonly javaIndex: string; readonly javaInstallation: string; readonly javaIntroduction: string; readonly javaRestLow: string; readonly jsAdvancedConfig: string; readonly jsApiReference: string; readonly jsBasicConfig: string; readonly jsClientConnecting: string; readonly jsIntro: string; readonly netGuide: string; readonly netIntroduction: string; readonly netNest: string; readonly netSingleNode: string; readonly phpConfiguration: string; readonly phpConnecting: string; readonly phpGuide: string; readonly phpInstallation: string; readonly phpOverview: string; readonly pythonAuthentication: string; readonly pythonConfig: string; readonly pythonConnecting: string; readonly pythonGuide: string; readonly pythonOverview: string; readonly rubyAuthentication: string; readonly rubyAdvancedConfig: string; readonly rubyBasicConfig: string; readonly rubyExamples: string; readonly rubyOverview: string; readonly rustGuide: string; readonly rustOverview: string; }" + "{ readonly guide: string; readonly goConnecting: string; readonly goGettingStarted: string; readonly goIndex: string; readonly goOverview: string; readonly javaBasicAuthentication: string; readonly javaIndex: string; readonly javaInstallation: string; readonly javaIntroduction: string; readonly javaRestLow: string; readonly jsAdvancedConfig: string; readonly jsApiReference: string; readonly jsBasicConfig: string; readonly jsClientConnecting: string; readonly jsIntro: string; readonly netGuide: string; readonly netIntroduction: string; readonly netNest: string; readonly netSingleNode: string; readonly phpConfiguration: string; readonly phpConnecting: string; readonly phpGuide: string; readonly phpInstallation: string; readonly phpOverview: string; readonly pythonAuthentication: string; readonly pythonConfig: string; readonly pythonConnecting: string; readonly pythonGuide: string; readonly pythonOverview: string; readonly rubyAuthentication: string; readonly rubyAdvancedConfig: string; readonly rubyBasicConfig: string; readonly rubyExamples: string; readonly rubyOverview: string; readonly rustGuide: string; readonly rustOverview: string; readonly eland: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index c2b5317f73d37..635e04c6fb4bf 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-11-22 +date: 2023-11-30 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 a4c8ca7d89c19..26830e06ed37b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 666ece0f24748..dc03a37458a27 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 23aea53234ce0..d95fd5beef4a6 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-11-22 +date: 2023-11-30 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 ccc4d354eb01b..2537fc6e76dc3 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-11-22 +date: 2023-11-30 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 8bfcd681b78a1..562bd8c3c6cfd 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.devdocs.json b/api_docs/kbn_elastic_agent_utils.devdocs.json index 84eff4c15229a..66197d89c54ba 100644 --- a/api_docs/kbn_elastic_agent_utils.devdocs.json +++ b/api_docs/kbn_elastic_agent_utils.devdocs.json @@ -431,7 +431,7 @@ "label": "AgentName", "description": [], "signature": [ - "\"java\" | \"go\" | \"dotnet\" | \"php\" | \"ruby\" | \"otlp\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"nodejs\" | \"python\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\"" + "\"java\" | \"ruby\" | \"go\" | \"dotnet\" | \"php\" | \"otlp\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"nodejs\" | \"python\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\"" ], "path": "packages/kbn-elastic-agent-utils/src/agent_names.ts", "deprecated": false, @@ -470,7 +470,7 @@ "\nWe cannot mark these arrays as const and derive their type\nbecause we need to be able to assign them as mutable entities for ES queries." ], "signature": [ - "\"java\" | \"go\" | \"dotnet\" | \"php\" | \"ruby\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"nodejs\" | \"python\"" + "\"java\" | \"ruby\" | \"go\" | \"dotnet\" | \"php\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"nodejs\" | \"python\"" ], "path": "packages/kbn-elastic-agent-utils/src/agent_names.ts", "deprecated": false, diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 123bb05e5efcf..0205eee7c609f 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index d308ab08ff13a..160c43608057c 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -1023,6 +1023,20 @@ "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant", + "id": "def-public.Message.traceData", + "type": "Object", + "tags": [], + "label": "traceData", + "description": [], + "signature": [ + "{ transactionId: string; traceId: string; } | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index de0ab6d7122c8..e24e8838b51aa 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 96 | 0 | 76 | 6 | +| 97 | 0 | 77 | 6 | ## Client diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 58dffab6be997..c89eca005394e 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-11-22 +date: 2023-11-30 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 d36e60db44eee..31b13b67c348b 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-11-22 +date: 2023-11-30 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 55f1263be6fac..94d976f79f62f 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 2da8cfc35d41e..2ae012946a9ce 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -1616,6 +1616,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.cleanupESQLQueryForLensSuggestions", + "type": "Function", + "tags": [], + "label": "cleanupESQLQueryForLensSuggestions", + "description": [], + "signature": [ + "(esql: string | undefined) => string" + ], + "path": "packages/kbn-es-query/src/es_query/es_aggregate_query.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.cleanupESQLQueryForLensSuggestions.$1", + "type": "string", + "tags": [], + "label": "esql", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-es-query/src/es_query/es_aggregate_query.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.compareFilters", diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 5ef40f350dc03..faf29ba51cee4 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 259 | 1 | 199 | 15 | +| 261 | 1 | 201 | 15 | ## Common diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 5d7bc87ecb8ef..56964117217f9 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-11-22 +date: 2023-11-30 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 4798ec416cbf8..95c93d573b643 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 9503e38fc9a21..d132272196907 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 131130f5dbca1..9e95a92f3c220 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 4e1972d116090..45dd6c7212464 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 93e963aa998d3..83e072481f134 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index ebdea05d8d39c..e4dca45ac4b72 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index dcc16591ee3c0..e29168013d1b8 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-11-22 +date: 2023-11-30 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 f08eb8a639095..a3138c1241d79 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-11-22 +date: 2023-11-30 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 d71921b4a2587..586c15b08046f 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 6fd098e8bea96..7f7b12d44d331 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 06328c7f2f437..ac140c0307a45 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 454f2b6a58833..6ed812b3a266d 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index d9613a0c95c7a..a84c2ea4d5781 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-11-22 +date: 2023-11-30 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 6fa0b6513d881..7ab4db1e524de 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-11-22 +date: 2023-11-30 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 767c8b81608c7..58b20cd1e94cc 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-11-22 +date: 2023-11-30 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 fb8490498c83e..522c96571cfbf 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-11-22 +date: 2023-11-30 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 54a3def92b849..955de6655dfbc 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-11-22 +date: 2023-11-30 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 098005b886049..527e25b2ea519 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-11-22 +date: 2023-11-30 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 c0e26362cdaa5..e39ee266f7db0 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-11-22 +date: 2023-11-30 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 ac22fc4f13fc0..09c4d08fedc2c 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 110c325a03a72..e2227727fea23 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index e0b685be0db39..25b7ebdc09a44 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-11-22 +date: 2023-11-30 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 a78da63778fec..7b43897ad2e5e 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-11-22 +date: 2023-11-30 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 f4dd095fb8a10..7e878243736b6 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-11-22 +date: 2023-11-30 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 0235cfb316e34..956d5a062ee2e 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-11-22 +date: 2023-11-30 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 3927e3b521f64..bdaae4e384fda 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-11-22 +date: 2023-11-30 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 8d5fb00bd0507..70b765256b534 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_language_documentation_popover.devdocs.json index efc5a267ed84c..d15c3a47f0f0b 100644 --- a/api_docs/kbn_language_documentation_popover.devdocs.json +++ b/api_docs/kbn_language_documentation_popover.devdocs.json @@ -27,7 +27,7 @@ "label": "LanguageDocumentationPopover", "description": [], "signature": [ - "React.NamedExoticComponent & { readonly type: ({ language, sections, buttonProps }: DocumentationPopoverProps) => JSX.Element; }" + "React.NamedExoticComponent & { readonly type: ({ language, sections, buttonProps, searchInDescription, }: DocumentationPopoverProps) => JSX.Element; }" ], "path": "packages/kbn-language-documentation-popover/src/components/documentation_popover.tsx", "deprecated": false, @@ -59,7 +59,7 @@ "label": "LanguageDocumentationPopoverContent", "description": [], "signature": [ - "React.NamedExoticComponent & { readonly type: ({ language, sections }: DocumentationProps) => JSX.Element; }" + "React.NamedExoticComponent & { readonly type: ({ language, sections, searchInDescription }: DocumentationProps) => JSX.Element; }" ], "path": "packages/kbn-language-documentation-popover/src/components/documentation_content.tsx", "deprecated": false, diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 5bf41edeb6ea0..18c78a1941839 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index b72975031363d..215ddca70a7aa 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 9cc5829d08ed3..88bbc6c8776ae 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-11-22 +date: 2023-11-30 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 03d94bb713f97..f508080eb9bdf 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-11-22 +date: 2023-11-30 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 497ee588a5bb9..eb42dd07b52c2 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index d2683dc97443e..047b9b9550268 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 7d0c5549a52ce..a29a4ced8c114 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 256fa84a2d160..31e8f7eab4037 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index f7e545d7d9fe8..8bc4d3e144909 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 9c7bb8938b61b..84e51274852aa 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 10484e7ae81aa..17ec1646f212a 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index b357286d2baf5..a4b7ca08fe65d 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 1158621472e97..11d5ede436e8c 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index e885c1a932eb8..7518e23e0a1d9 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index e7c9a31638751..df1fbf06b9ce3 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 4c960a9c01890..cb7f6b293ceee 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 9ff819a3e7026..ca5d58d41c645 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.devdocs.json b/api_docs/kbn_mapbox_gl.devdocs.json index 18b71846c478e..838e1f65579ce 100644 --- a/api_docs/kbn_mapbox_gl.devdocs.json +++ b/api_docs/kbn_mapbox_gl.devdocs.json @@ -9009,7 +9009,7 @@ "label": "sourceDataType", "description": [], "signature": [ - "\"content\" | \"visibility\" | \"metadata\" | \"idle\"" + "\"content\" | \"metadata\" | \"visibility\" | \"idle\"" ], "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index e0bdbbe1eba64..3875c9302ac74 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 44cbf2d0175cf..8a1ed49a26abf 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 2c64158ddec87..9a5c7eda8d197 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index c9a9df6b61779..72e2ee3636ed2 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 26d241d6483c9..bfabd0c6aec2f 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index fcc6bf44d43c5..f232b83861ff3 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.devdocs.json b/api_docs/kbn_ml_data_frame_analytics_utils.devdocs.json index c306defc55a5c..2a24271056761 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.devdocs.json +++ b/api_docs/kbn_ml_data_frame_analytics_utils.devdocs.json @@ -2417,7 +2417,7 @@ "\nUnion type of JOB_MAP_NODE_TYPES" ], "signature": [ - "\"index\" | \"transform\" | \"analytics\" | \"trainedModel\" | \"ingestPipeline\"" + "\"index\" | \"transform\" | \"analytics\" | \"analytics-job-missing\" | \"trainedModel\" | \"ingestPipeline\"" ], "path": "x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts", "deprecated": false, @@ -2742,7 +2742,7 @@ "\nCustom enum for job map node types for the DFA map view" ], "signature": [ - "{ readonly ANALYTICS: \"analytics\"; readonly TRANSFORM: \"transform\"; readonly INDEX: \"index\"; readonly TRAINED_MODEL: \"trainedModel\"; readonly INGEST_PIPELINE: \"ingestPipeline\"; }" + "{ readonly ANALYTICS: \"analytics\"; readonly ANALYTICS_JOB_MISSING: \"analytics-job-missing\"; readonly TRANSFORM: \"transform\"; readonly INDEX: \"index\"; readonly TRAINED_MODEL: \"trainedModel\"; readonly INGEST_PIPELINE: \"ingestPipeline\"; }" ], "path": "x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts", "deprecated": false, diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index b21202528baf0..edc981befac48 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.devdocs.json b/api_docs/kbn_ml_data_grid.devdocs.json index 0e63c9f639c1c..14fcad6d9b0ad 100644 --- a/api_docs/kbn_ml_data_grid.devdocs.json +++ b/api_docs/kbn_ml_data_grid.devdocs.json @@ -347,10 +347,10 @@ }, { "parentPluginId": "@kbn/ml-data-grid", - "id": "def-common.getFieldsFromKibanaIndexPattern", + "id": "def-common.getFieldsFromKibanaDataView", "type": "Function", "tags": [], - "label": "getFieldsFromKibanaIndexPattern", + "label": "getFieldsFromKibanaDataView", "description": [ "\nRetrieves fields from a Kibana data view." ], @@ -371,7 +371,7 @@ "children": [ { "parentPluginId": "@kbn/ml-data-grid", - "id": "def-common.getFieldsFromKibanaIndexPattern.$1", + "id": "def-common.getFieldsFromKibanaDataView.$1", "type": "Object", "tags": [], "label": "dataView", @@ -2489,12 +2489,12 @@ }, { "parentPluginId": "@kbn/ml-data-grid", - "id": "def-common.UseIndexDataReturnType.indexPatternFields", + "id": "def-common.UseIndexDataReturnType.dataViewFields", "type": "Array", "tags": [], - "label": "indexPatternFields", + "label": "dataViewFields", "description": [ - "\nOptional index pattern fields." + "\nOptional data view fields." ], "signature": [ "string[] | undefined" diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index a232bbca14d8c..a3b02f066faff 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index b6d0ad698d800..72ecf9cf3af13 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index a63bc9a9b9fad..bf7373d3ac5e3 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index e1c6542c8b369..1f962e627b0bb 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 32b5a939c68f5..a9a8ff3a0fd52 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 7647efc426ac8..4fbcccff05654 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-11-22 +date: 2023-11-30 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 23a1e3286f231..8a74551d5f45f 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index a13c7ea2feb45..2ba2dadb3467a 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index cb9c909fd35de..857fb78d44801 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-11-22 +date: 2023-11-30 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 815bd15dbdc7a..ab97c60353baf 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 7a629ab0d1461..8d15714382878 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 07148979176c4..9223d85f80247 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 9f42499f996e8..ba7af23a85fb4 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 70f27ff011780..fc0a7748ac29e 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index a1d8509efc67b..726adc938ccf1 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 69e7d86fbc836..16cf5840d0716 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.devdocs.json b/api_docs/kbn_ml_trained_models_utils.devdocs.json index d2bb275d5a25d..c4ec8300daf97 100644 --- a/api_docs/kbn_ml_trained_models_utils.devdocs.json +++ b/api_docs/kbn_ml_trained_models_utils.devdocs.json @@ -72,7 +72,9 @@ "type": "string", "tags": [], "label": "modelName", - "description": [], + "description": [ + "\nModel name, e.g. elser" + ], "path": "x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts", "deprecated": false, "trackAdoption": false @@ -94,7 +96,9 @@ "type": "Uncategorized", "tags": [], "label": "config", - "description": [], + "description": [ + "\nDefault PUT model configuration" + ], "signature": [ "object" ], @@ -314,7 +318,6 @@ "label": "ModelDefinitionResponse", "description": [], "signature": [ - "Omit<", { "pluginId": "@kbn/ml-trained-models-utils", "scope": "common", @@ -322,7 +325,7 @@ "section": "def-common.ModelDefinition", "text": "ModelDefinition" }, - ", \"modelName\"> & { name: string; }" + " & { name: string; }" ], "path": "x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts", "deprecated": false, diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 87ad0e92b6fad..7f33035fb9ee8 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.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 | |-------------------|-----------|------------------------|-----------------| -| 29 | 0 | 29 | 0 | +| 29 | 0 | 27 | 0 | ## Common diff --git a/api_docs/kbn_ml_ui_actions.devdocs.json b/api_docs/kbn_ml_ui_actions.devdocs.json index 4bf80b6ae59a1..6fe15ef82553e 100644 --- a/api_docs/kbn_ml_ui_actions.devdocs.json +++ b/api_docs/kbn_ml_ui_actions.devdocs.json @@ -20,6 +20,85 @@ "classes": [], "functions": [], "interfaces": [ + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CategorizeFieldContext", + "type": "Interface", + "tags": [], + "label": "CategorizeFieldContext", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CategorizeFieldContext.field", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + } + ], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CategorizeFieldContext.dataView", + "type": "Object", + "tags": [], + "label": "dataView", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + } + ], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CategorizeFieldContext.originatingApp", + "type": "string", + "tags": [], + "label": "originatingApp", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CategorizeFieldContext.additionalFilter", + "type": "Object", + "tags": [], + "label": "additionalFilter", + "description": [], + "signature": [ + "{ from: number; to: number; field?: { name: string; value: string; } | undefined; } | undefined" + ], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/ml-ui-actions", "id": "def-common.CreateCategorizationADJobContext", @@ -27,7 +106,7 @@ "tags": [], "label": "CreateCategorizationADJobContext", "description": [], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -47,7 +126,7 @@ "text": "DataViewField" } ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false }, @@ -67,7 +146,7 @@ "text": "DataView" } ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false }, @@ -81,7 +160,7 @@ "signature": [ "QueryDslQueryContainer" ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false }, @@ -95,7 +174,7 @@ "signature": [ "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false } @@ -105,6 +184,36 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.ACTION_CATEGORIZE_FIELD", + "type": "string", + "tags": [], + "label": "ACTION_CATEGORIZE_FIELD", + "description": [], + "signature": [ + "\"ACTION_CATEGORIZE_FIELD\"" + ], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.CATEGORIZE_FIELD_TRIGGER", + "type": "string", + "tags": [], + "label": "CATEGORIZE_FIELD_TRIGGER", + "description": [], + "signature": [ + "\"CATEGORIZE_FIELD_TRIGGER\"" + ], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/ml-ui-actions", "id": "def-common.CREATE_PATTERN_ANALYSIS_TO_ML_AD_JOB_ACTION", @@ -115,7 +224,7 @@ "signature": [ "\"createMLADCategorizationJobAction\"" ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -130,12 +239,60 @@ "signature": [ "\"CREATE_PATTERN_ANALYSIS_TO_ML_AD_JOB_TRIGGER\"" ], - "path": "x-pack/packages/ml/ui_actions/src/ui_actions.ts", + "path": "x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false } ], - "objects": [] + "objects": [ + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.categorizeFieldTrigger", + "type": "Object", + "tags": [], + "label": "categorizeFieldTrigger", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.categorizeFieldTrigger.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.categorizeFieldTrigger.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-ui-actions", + "id": "def-common.categorizeFieldTrigger.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 38acff5d6d0f7..8cc56f0fde91c 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; @@ -21,10 +21,13 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 7 | 0 | 7 | 0 | +| 18 | 0 | 18 | 0 | ## Common +### Objects + + ### Interfaces diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index dd8996fb81355..dc802946776d4 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-11-22 +date: 2023-11-30 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 b00f77ec4a8de..d575e02197160 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 4bf9b3616bbf8..839eced0d1936 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 042c18f6805d1..e77b3c9415532 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.devdocs.json b/api_docs/kbn_observability_alerting_test_data.devdocs.json index 96da803aa4b60..d75f51f28846a 100644 --- a/api_docs/kbn_observability_alerting_test_data.devdocs.json +++ b/api_docs/kbn_observability_alerting_test_data.devdocs.json @@ -434,7 +434,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; filter: string; aggType: ", "Aggregators", @@ -597,7 +597,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; filter: string; aggType: ", "Aggregators", @@ -774,7 +774,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; filter: string; aggType: ", "Aggregators", @@ -937,7 +937,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; field: string; aggType: ", "Aggregators", @@ -1100,7 +1100,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; field: string; aggType: ", "Aggregators", @@ -1277,7 +1277,7 @@ "label": "criteria", "description": [], "signature": [ - "{ aggType: string; comparator: ", + "{ comparator: ", "Comparator", "; threshold: number[]; timeSize: number; timeUnit: string; metrics: { name: string; field: string; aggType: ", "Aggregators", diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 55e37306f7e5e..29d921436e9ba 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.devdocs.json b/api_docs/kbn_openapi_bundler.devdocs.json new file mode 100644 index 0000000000000..bfb8dad96559b --- /dev/null +++ b/api_docs/kbn_openapi_bundler.devdocs.json @@ -0,0 +1,123 @@ +{ + "id": "@kbn/openapi-bundler", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.bundle", + "type": "Function", + "tags": [], + "label": "bundle", + "description": [], + "signature": [ + "(config: ", + { + "pluginId": "@kbn/openapi-bundler", + "scope": "common", + "docId": "kibKbnOpenapiBundlerPluginApi", + "section": "def-common.BundlerConfig", + "text": "BundlerConfig" + }, + ") => Promise" + ], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.bundle.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + { + "pluginId": "@kbn/openapi-bundler", + "scope": "common", + "docId": "kibKbnOpenapiBundlerPluginApi", + "section": "def-common.BundlerConfig", + "text": "BundlerConfig" + } + ], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.BundlerConfig", + "type": "Interface", + "tags": [], + "label": "BundlerConfig", + "description": [], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.BundlerConfig.rootDir", + "type": "string", + "tags": [], + "label": "rootDir", + "description": [], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.BundlerConfig.sourceGlob", + "type": "string", + "tags": [], + "label": "sourceGlob", + "description": [], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/openapi-bundler", + "id": "def-common.BundlerConfig.outputFilePath", + "type": "string", + "tags": [], + "label": "outputFilePath", + "description": [], + "path": "packages/kbn-openapi-bundler/src/openapi_bundler.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx new file mode 100644 index 0000000000000..55380c62dc322 --- /dev/null +++ b/api_docs/kbn_openapi_bundler.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: kibKbnOpenapiBundlerPluginApi +slug: /kibana-dev-docs/api/kbn-openapi-bundler +title: "@kbn/openapi-bundler" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/openapi-bundler plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] +--- +import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; + + + +Contact [@elastic/security-detection-rule-management](https://github.com/orgs/elastic/teams/security-detection-rule-management) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 6 | 0 | 6 | 0 | + +## Common + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 5a9e16e8306ce..ee8b1e8c8e28b 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 055ccb709afe1..7d3bd57cd7210 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-11-22 +date: 2023-11-30 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 cac0563399760..f31d70b04c3ab 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-11-22 +date: 2023-11-30 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 c4512d0e5c014..0cdc03682e66e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.devdocs.json b/api_docs/kbn_panel_loader.devdocs.json new file mode 100644 index 0000000000000..5282b2609136d --- /dev/null +++ b/api_docs/kbn_panel_loader.devdocs.json @@ -0,0 +1,87 @@ +{ + "id": "@kbn/panel-loader", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/panel-loader", + "id": "def-public.PanelLoader", + "type": "Function", + "tags": [], + "label": "PanelLoader", + "description": [], + "signature": [ + "(props: { showShadow?: boolean | undefined; dataTestSubj?: string | undefined; }) => JSX.Element" + ], + "path": "packages/kbn-panel-loader/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/panel-loader", + "id": "def-public.PanelLoader.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "path": "packages/kbn-panel-loader/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/panel-loader", + "id": "def-public.PanelLoader.$1.showShadow", + "type": "CompoundType", + "tags": [], + "label": "showShadow", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-panel-loader/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/panel-loader", + "id": "def-public.PanelLoader.$1.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-panel-loader/index.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx new file mode 100644 index 0000000000000..1beaa0f954e67 --- /dev/null +++ b/api_docs/kbn_panel_loader.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnPanelLoaderPluginApi +slug: /kibana-dev-docs/api/kbn-panel-loader +title: "@kbn/panel-loader" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/panel-loader plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] +--- +import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; + + + +Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 0 | + +## Client + +### Functions + + diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index f2e97eb56b674..df168563f0ba5 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-11-22 +date: 2023-11-30 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 81fba7582d142..10717334d1710 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-11-22 +date: 2023-11-30 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 d024231191180..875201931aa24 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.devdocs.json b/api_docs/kbn_profiling_utils.devdocs.json index 597124dd75175..f49a43e57c1f9 100644 --- a/api_docs/kbn_profiling_utils.devdocs.json +++ b/api_docs/kbn_profiling_utils.devdocs.json @@ -2699,6 +2699,18 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/profiling-utils", + "id": "def-common.TopNComparisonFunctionSortField", + "type": "Enum", + "tags": [], + "label": "TopNComparisonFunctionSortField", + "description": [], + "path": "packages/kbn-profiling-utils/common/functions.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/profiling-utils", "id": "def-common.TopNFunctionSortField", @@ -2990,6 +3002,82 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/profiling-utils", + "id": "def-common.topNComparisonFunctionSortFieldRt", + "type": "Object", + "tags": [], + "label": "topNComparisonFunctionSortFieldRt", + "description": [], + "signature": [ + "UnionC", + "<[", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonRank>, ", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonFrame>, ", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonSamples>, ", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonSelfCPU>, ", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonTotalCPU>, ", + "LiteralC", + "<", + { + "pluginId": "@kbn/profiling-utils", + "scope": "common", + "docId": "kibKbnProfilingUtilsPluginApi", + "section": "def-common.TopNComparisonFunctionSortField", + "text": "TopNComparisonFunctionSortField" + }, + ".ComparisonDiff>]>" + ], + "path": "packages/kbn-profiling-utils/common/functions.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/profiling-utils", "id": "def-common.topNFunctionSortFieldRt", diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 5b4f8e25fb87a..2292213a4528c 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 171 | 0 | 28 | 0 | +| 173 | 0 | 30 | 0 | ## Common diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 24ff60cbf6882..1277576052e08 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index cc435f09a3fc1..5ffca3d430ba1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 811539d597b2c..9087a805d657b 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index e0ae9c971de90..59b23f6df8109 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 7f662ebeeb88b..ce31c33462da8 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 826dc9f68072c..1d92959338903 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index ae0ac813ef76f..540c460f9813b 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 2e5a9ee14c25d..daadb05f5f41d 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index ba2aa2fa122dd..a69c63ada442b 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-11-22 +date: 2023-11-30 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 aca00f31e0167..34891592a786a 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-11-22 +date: 2023-11-30 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 5bf26ada810f9..74d12575c10d6 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-11-22 +date: 2023-11-30 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 d9a78087a5ba8..ab784f18c1285 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 9f8338af2b5da..8b8407d4bf763 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index a0cee98c3051d..c59eefbe104a6 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 9d0e0a218a002..9f0a7e4255f87 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index f2ac105c7cb75..01805a6bdead6 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 57701e2dbb62f..85be70e428046 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 9f5813b26e5b7..0c0ea5b037ffd 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index 0e81d91701895..96172179c1975 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index a9ba58b1c6457..dbb5afce62d84 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 0c1f3decb896e..a142738f435f0 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index cd2b881a22123..0830f80caaec9 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index bb173c0d90b12..015b247822184 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 2b6ecaedc8219..a95c89241eb74 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 176f37131a535..62da68f9f93b8 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index b11d995e5c561..0bb1a080f2441 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index c7dd438e68628..0133a8963358d 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.devdocs.json b/api_docs/kbn_search_api_panels.devdocs.json index 8558d593d4290..8013e6c49470b 100644 --- a/api_docs/kbn_search_api_panels.devdocs.json +++ b/api_docs/kbn_search_api_panels.devdocs.json @@ -279,7 +279,7 @@ "label": "IngestData", "description": [], "signature": [ - "({ codeSnippet, selectedLanguage, setSelectedLanguage, docLinks, assetBasePath, application, sharePlugin, languages, consoleRequest, }: React.PropsWithChildren) => JSX.Element" + "({ codeSnippet, selectedLanguage, setSelectedLanguage, docLinks, assetBasePath, application, sharePlugin, languages, consoleRequest, additionalIngestionPanel, }: React.PropsWithChildren) => JSX.Element" ], "path": "packages/kbn-search-api-panels/components/ingest_data.tsx", "deprecated": false, @@ -290,7 +290,7 @@ "id": "def-common.IngestData.$1", "type": "CompoundType", "tags": [], - "label": "{\n codeSnippet,\n selectedLanguage,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n sharePlugin,\n languages,\n consoleRequest,\n}", + "label": "{\n codeSnippet,\n selectedLanguage,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n sharePlugin,\n languages,\n consoleRequest,\n additionalIngestionPanel,\n}", "description": [], "signature": [ "React.PropsWithChildren" @@ -306,29 +306,29 @@ }, { "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.InstallClientPanel", + "id": "def-common.IngestionsPanel", "type": "Function", "tags": [], - "label": "InstallClientPanel", + "label": "IngestionsPanel", "description": [], "signature": [ - "({ codeSnippet, consoleRequest, language, languages, setSelectedLanguage, assetBasePath, application, sharePlugin, isPanelLeft, overviewPanelProps, }: React.PropsWithChildren) => JSX.Element" + "({ additionalIngestionPanel, docLinks, assetBasePath, }: React.PropsWithChildren) => JSX.Element" ], - "path": "packages/kbn-search-api-panels/components/install_client.tsx", + "path": "packages/kbn-search-api-panels/components/ingestions_panel.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.InstallClientPanel.$1", + "id": "def-common.IngestionsPanel.$1", "type": "CompoundType", "tags": [], - "label": "{\n codeSnippet,\n consoleRequest,\n language,\n languages,\n setSelectedLanguage,\n assetBasePath,\n application,\n sharePlugin,\n isPanelLeft = true,\n overviewPanelProps,\n}", + "label": "{\n additionalIngestionPanel,\n docLinks,\n assetBasePath,\n}", "description": [], "signature": [ - "React.PropsWithChildren" + "React.PropsWithChildren" ], - "path": "packages/kbn-search-api-panels/components/install_client.tsx", + "path": "packages/kbn-search-api-panels/components/ingestions_panel.tsx", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -339,45 +339,29 @@ }, { "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.IntegrationsPanel", + "id": "def-common.InstallClientPanel", "type": "Function", "tags": [], - "label": "IntegrationsPanel", + "label": "InstallClientPanel", "description": [], "signature": [ - "({ docLinks, assetBasePath, }: React.PropsWithChildren<", - { - "pluginId": "@kbn/search-api-panels", - "scope": "common", - "docId": "kibKbnSearchApiPanelsPluginApi", - "section": "def-common.IntegrationsPanelProps", - "text": "IntegrationsPanelProps" - }, - ">) => JSX.Element" + "({ codeSnippet, consoleRequest, language, languages, setSelectedLanguage, assetBasePath, application, sharePlugin, isPanelLeft, overviewPanelProps, }: React.PropsWithChildren) => JSX.Element" ], - "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", + "path": "packages/kbn-search-api-panels/components/install_client.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.IntegrationsPanel.$1", + "id": "def-common.InstallClientPanel.$1", "type": "CompoundType", "tags": [], - "label": "{\n docLinks,\n assetBasePath,\n}", + "label": "{\n codeSnippet,\n consoleRequest,\n language,\n languages,\n setSelectedLanguage,\n assetBasePath,\n application,\n sharePlugin,\n isPanelLeft = true,\n overviewPanelProps,\n}", "description": [], "signature": [ - "React.PropsWithChildren<", - { - "pluginId": "@kbn/search-api-panels", - "scope": "common", - "docId": "kibKbnSearchApiPanelsPluginApi", - "section": "def-common.IntegrationsPanelProps", - "text": "IntegrationsPanelProps" - }, - ">" + "React.PropsWithChildren" ], - "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", + "path": "packages/kbn-search-api-panels/components/install_client.tsx", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -599,45 +583,6 @@ } ], "interfaces": [ - { - "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.IntegrationsPanelProps", - "type": "Interface", - "tags": [], - "label": "IntegrationsPanelProps", - "description": [], - "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.IntegrationsPanelProps.docLinks", - "type": "Object", - "tags": [], - "label": "docLinks", - "description": [], - "signature": [ - "{ beats: string; connectors: string; logstash: string; }" - ], - "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-api-panels", - "id": "def-common.IntegrationsPanelProps.assetBasePath", - "type": "string", - "tags": [], - "label": "assetBasePath", - "description": [], - "path": "packages/kbn-search-api-panels/components/integrations_panel.tsx", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.LanguageDefinition", @@ -1166,9 +1111,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 322e0f73469de..be7d7b44041e3 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 69 | 0 | 69 | 0 | +| 66 | 0 | 66 | 0 | ## Common diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 0cd9ab0486c86..cbdac106bdab6 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 2584fa0deaacf..38da50b0435e2 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.devdocs.json b/api_docs/kbn_security_plugin_types_common.devdocs.json new file mode 100644 index 0000000000000..711bd3fec69e0 --- /dev/null +++ b/api_docs/kbn_security_plugin_types_common.devdocs.json @@ -0,0 +1,1370 @@ +{ + "id": "@kbn/security-plugin-types-common", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser", + "type": "Interface", + "tags": [], + "label": "AuthenticatedUser", + "description": [ + "\nRepresents the currently authenticated user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.User", + "text": "User" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.authentication_realm", + "type": "Object", + "tags": [], + "label": "authentication_realm", + "description": [ + "\nThe name and type of the Realm that has authenticated the user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserRealm", + "text": "UserRealm" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.lookup_realm", + "type": "Object", + "tags": [], + "label": "lookup_realm", + "description": [ + "\nThe name and type of the Realm where the user information were retrieved from." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserRealm", + "text": "UserRealm" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.authentication_provider", + "type": "Object", + "tags": [], + "label": "authentication_provider", + "description": [ + "\nThe authentication provider that used to authenticate user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticationProvider", + "text": "AuthenticationProvider" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.authentication_type", + "type": "string", + "tags": [], + "label": "authentication_type", + "description": [ + "\nThe AuthenticationType used by ES to authenticate the user.\n" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.elastic_cloud_user", + "type": "boolean", + "tags": [], + "label": "elastic_cloud_user", + "description": [ + "\nIndicates whether user is authenticated via Elastic Cloud built-in SAML realm." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticatedUser.profile_uid", + "type": "string", + "tags": [], + "label": "profile_uid", + "description": [ + "\nUser profile ID of this user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticationProvider", + "type": "Interface", + "tags": [], + "label": "AuthenticationProvider", + "description": [ + "\nType and name tuple to identify provider used to authenticate user." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticationProvider.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "\nType of the Kibana authentication provider." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.AuthenticationProvider.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nName of the Kibana authentication provider (arbitrary string)." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.FeaturesPrivileges", + "type": "Interface", + "tags": [], + "label": "FeaturesPrivileges", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.FeaturesPrivileges.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[featureId: string]: string[]", + "description": [], + "signature": [ + "[featureId: string]: string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role", + "type": "Interface", + "tags": [], + "label": "Role", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [], + "signature": [ + "{ cluster: string[]; indices: ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleIndexPrivilege", + "text": "RoleIndexPrivilege" + }, + "[]; remote_indices?: ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleRemoteIndexPrivilege", + "text": "RoleRemoteIndexPrivilege" + }, + "[] | undefined; run_as: string[]; }" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role.kibana", + "type": "Array", + "tags": [], + "label": "kibana", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleKibanaPrivilege", + "text": "RoleKibanaPrivilege" + }, + "[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [anyKey: string]: any; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role.transient_metadata", + "type": "Object", + "tags": [], + "label": "transient_metadata", + "description": [], + "signature": [ + "{ [anyKey: string]: any; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role._transform_error", + "type": "Array", + "tags": [], + "label": "_transform_error", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.Role._unrecognized_applications", + "type": "Array", + "tags": [], + "label": "_unrecognized_applications", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleIndexPrivilege", + "type": "Interface", + "tags": [], + "label": "RoleIndexPrivilege", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleIndexPrivilege.names", + "type": "Array", + "tags": [], + "label": "names", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleIndexPrivilege.privileges", + "type": "Array", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleIndexPrivilege.field_security", + "type": "Object", + "tags": [], + "label": "field_security", + "description": [], + "signature": [ + "{ grant?: string[] | undefined; except?: string[] | undefined; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleIndexPrivilege.query", + "type": "string", + "tags": [], + "label": "query", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleKibanaPrivilege", + "type": "Interface", + "tags": [], + "label": "RoleKibanaPrivilege", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleKibanaPrivilege.spaces", + "type": "Array", + "tags": [], + "label": "spaces", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleKibanaPrivilege.base", + "type": "Array", + "tags": [], + "label": "base", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleKibanaPrivilege.feature", + "type": "Object", + "tags": [], + "label": "feature", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.FeaturesPrivileges", + "text": "FeaturesPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleKibanaPrivilege._reserved", + "type": "Array", + "tags": [], + "label": "_reserved", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleRemoteIndexPrivilege", + "type": "Interface", + "tags": [], + "label": "RoleRemoteIndexPrivilege", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleRemoteIndexPrivilege", + "text": "RoleRemoteIndexPrivilege" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleIndexPrivilege", + "text": "RoleIndexPrivilege" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.RoleRemoteIndexPrivilege.clusters", + "type": "Array", + "tags": [], + "label": "clusters", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense", + "type": "Interface", + "tags": [], + "label": "SecurityLicense", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.isLicenseAvailable", + "type": "Function", + "tags": [], + "label": "isLicenseAvailable", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.isEnabled", + "type": "Function", + "tags": [], + "label": "isEnabled", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.getFeatures", + "type": "Function", + "tags": [], + "label": "getFeatures", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.SecurityLicenseFeatures", + "text": "SecurityLicenseFeatures" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.hasAtLeast", + "type": "Function", + "tags": [], + "label": "hasAtLeast", + "description": [], + "signature": [ + "(licenseType: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\") => boolean | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.hasAtLeast.$1", + "type": "CompoundType", + "tags": [], + "label": "licenseType", + "description": [], + "signature": [ + "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicense.features$", + "type": "Object", + "tags": [], + "label": "features$", + "description": [], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.SecurityLicenseFeatures", + "text": "SecurityLicenseFeatures" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures", + "type": "Interface", + "tags": [], + "label": "SecurityLicenseFeatures", + "description": [ + "\nDescribes Security plugin features that depend on license." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.showLogin", + "type": "boolean", + "tags": [], + "label": "showLogin", + "description": [ + "\nIndicates whether we show login page or skip it." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowLogin", + "type": "boolean", + "tags": [], + "label": "allowLogin", + "description": [ + "\nIndicates whether we allow login or disable it on the login page." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.showLinks", + "type": "boolean", + "tags": [], + "label": "showLinks", + "description": [ + "\nIndicates whether we show security links throughout the kibana app." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.showRoleMappingsManagement", + "type": "boolean", + "tags": [], + "label": "showRoleMappingsManagement", + "description": [ + "\nIndicates whether we show the Role Mappings UI." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowAccessAgreement", + "type": "boolean", + "tags": [], + "label": "allowAccessAgreement", + "description": [ + "\nIndicates whether we allow users to access agreement UI and acknowledge it." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowAuditLogging", + "type": "boolean", + "tags": [], + "label": "allowAuditLogging", + "description": [ + "\nIndicates whether we allow logging of audit events." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowRoleDocumentLevelSecurity", + "type": "boolean", + "tags": [], + "label": "allowRoleDocumentLevelSecurity", + "description": [ + "\nIndicates whether we allow users to define document level security in roles." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowRoleFieldLevelSecurity", + "type": "boolean", + "tags": [], + "label": "allowRoleFieldLevelSecurity", + "description": [ + "\nIndicates whether we allow users to define field level security in roles." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowRoleRemoteIndexPrivileges", + "type": "boolean", + "tags": [], + "label": "allowRoleRemoteIndexPrivileges", + "description": [ + "\nIndicates whether we allow users to define remote index privileges in roles." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowRbac", + "type": "boolean", + "tags": [], + "label": "allowRbac", + "description": [ + "\nIndicates whether we allow Role-based access control (RBAC)." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowSubFeaturePrivileges", + "type": "boolean", + "tags": [], + "label": "allowSubFeaturePrivileges", + "description": [ + "\nIndicates whether we allow sub-feature privileges." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.allowUserProfileCollaboration", + "type": "boolean", + "tags": [], + "label": "allowUserProfileCollaboration", + "description": [ + "\nIndicates whether we allow user profile collaboration features (suggest and privileges checks APIs)." + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.SecurityLicenseFeatures.layout", + "type": "CompoundType", + "tags": [], + "label": "layout", + "description": [ + "\nDescribes the layout of the login form if it's displayed." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.LoginLayout", + "text": "LoginLayout" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User", + "type": "Interface", + "tags": [], + "label": "User", + "description": [ + "\nA set of fields describing Kibana user." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.email", + "type": "string", + "tags": [], + "label": "email", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.full_name", + "type": "string", + "tags": [], + "label": "full_name", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.roles", + "type": "Object", + "tags": [], + "label": "roles", + "description": [], + "signature": [ + "readonly string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.User.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ _reserved: boolean; _deprecated?: boolean | undefined; _deprecated_reason?: string | undefined; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfile", + "type": "Interface", + "tags": [], + "label": "UserProfile", + "description": [ + "\nIMPORTANT:\n\nThe types in this file are duplicated at\n`packages/kbn-user-profile-components/src/user_profile.ts`\n\nWhen making changes please ensure to keep both files in sync.\n\nDescribes basic properties stored in user profile." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfile.uid", + "type": "string", + "tags": [], + "label": "uid", + "description": [ + "\nUnique ID for of the user profile." + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfile.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nIndicates whether user profile is enabled or not." + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfile.user", + "type": "Object", + "tags": [], + "label": "user", + "description": [ + "\nInformation about the user that owns profile." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileUserInfo", + "text": "UserProfileUserInfo" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfile.data", + "type": "Object", + "tags": [], + "label": "data", + "description": [ + "\nUser specific data associated with the profile." + ], + "signature": [ + "{ [P in keyof D]?: D[P] | undefined; }" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfo", + "type": "Interface", + "tags": [], + "label": "UserProfileUserInfo", + "description": [ + "\nBasic user information returned in user profile." + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfo.username", + "type": "string", + "tags": [], + "label": "username", + "description": [ + "\nUsername of the user." + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfo.email", + "type": "string", + "tags": [], + "label": "email", + "description": [ + "\nOptional email of the user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfo.full_name", + "type": "string", + "tags": [], + "label": "full_name", + "description": [ + "\nOptional full name of the user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfoWithSecurity", + "type": "Interface", + "tags": [], + "label": "UserProfileUserInfoWithSecurity", + "description": [ + "\nExtended user information returned in user profile (both basic and security related properties)." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileUserInfoWithSecurity", + "text": "UserProfileUserInfoWithSecurity" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileUserInfo", + "text": "UserProfileUserInfo" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfoWithSecurity.roles", + "type": "Object", + "tags": [], + "label": "roles", + "description": [ + "\nList of the user roles." + ], + "signature": [ + "readonly string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfoWithSecurity.realm_name", + "type": "string", + "tags": [], + "label": "realm_name", + "description": [ + "\nName of the Elasticsearch security realm that was used to authenticate user." + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileUserInfoWithSecurity.realm_domain", + "type": "string", + "tags": [], + "label": "realm_domain", + "description": [ + "\nOptional name of the security domain that Elasticsearch security realm that was\nused to authenticate user resides in (if any)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileWithSecurity", + "type": "Interface", + "tags": [], + "label": "UserProfileWithSecurity", + "description": [ + "\nDescribes all properties stored in user profile (both basic and security related properties)." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileWithSecurity", + "text": "UserProfileWithSecurity" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileWithSecurity.user", + "type": "Object", + "tags": [], + "label": "user", + "description": [ + "\nInformation about the user that owns profile." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileUserInfoWithSecurity", + "text": "UserProfileUserInfoWithSecurity" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileWithSecurity.labels", + "type": "Uncategorized", + "tags": [], + "label": "labels", + "description": [ + "\nUser specific _searchable_ labels associated with the profile. Note that labels are considered\nsecurity related field since it's going to be used to store user's space ID." + ], + "signature": [ + "L" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserRealm", + "type": "Interface", + "tags": [], + "label": "UserRealm", + "description": [ + "\nAn Elasticsearch realm that was used to resolve and authenticate the user." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserRealm.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nArbitrary name of the security realm." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserRealm.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "\nType of the security realm (file, native, saml etc.)." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.LoginLayout", + "type": "Type", + "tags": [], + "label": "LoginLayout", + "description": [ + "\nRepresents types of login form layouts." + ], + "signature": [ + "\"form\" | \"error-es-unavailable\" | \"error-xpack-unavailable\"" + ], + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileData", + "type": "Type", + "tags": [], + "label": "UserProfileData", + "description": [ + "\nPlaceholder for data stored in user profile." + ], + "signature": [ + "{ [x: string]: unknown; }" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-common", + "id": "def-common.UserProfileLabels", + "type": "Type", + "tags": [], + "label": "UserProfileLabels", + "description": [ + "\nType of the user profile labels structure (currently" + ], + "signature": [ + "{ [x: string]: string; }" + ], + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx new file mode 100644 index 0000000000000..3e2bb12fd01ed --- /dev/null +++ b/api_docs/kbn_security_plugin_types_common.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: kibKbnSecurityPluginTypesCommonPluginApi +slug: /kibana-dev-docs/api/kbn-security-plugin-types-common +title: "@kbn/security-plugin-types-common" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/security-plugin-types-common plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] +--- +import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; + + + +Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 82 | 0 | 35 | 0 | + +## Common + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_security_plugin_types_public.devdocs.json b/api_docs/kbn_security_plugin_types_public.devdocs.json new file mode 100644 index 0000000000000..e37a5f5d84829 --- /dev/null +++ b/api_docs/kbn_security_plugin_types_public.devdocs.json @@ -0,0 +1,939 @@ +{ + "id": "@kbn/security-plugin-types-public", + "client": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.AuthenticationServiceSetup", + "type": "Interface", + "tags": [], + "label": "AuthenticationServiceSetup", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.AuthenticationServiceSetup.getCurrentUser", + "type": "Function", + "tags": [], + "label": "getCurrentUser", + "description": [ + "\nReturns currently authenticated user and throws if current user isn't authenticated." + ], + "signature": [ + "() => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.AuthenticationServiceSetup.areAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areAPIKeysEnabled", + "description": [ + "\nDetermines if API Keys are currently enabled." + ], + "signature": [ + "() => Promise" + ], + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.GetUserProfileResponse", + "type": "Interface", + "tags": [], + "label": "GetUserProfileResponse", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.GetUserProfileResponse", + "text": "GetUserProfileResponse" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileWithSecurity", + "text": "UserProfileWithSecurity" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.GetUserProfileResponse.user", + "type": "CompoundType", + "tags": [], + "label": "user", + "description": [ + "\nInformation about the currently authenticated user that owns the profile." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileUserInfoWithSecurity", + "text": "UserProfileUserInfoWithSecurity" + }, + " & Pick<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + ", \"authentication_provider\">" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityNavControlServiceStart", + "type": "Interface", + "tags": [], + "label": "SecurityNavControlServiceStart", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityNavControlServiceStart.getUserMenuLinks$", + "type": "Function", + "tags": [], + "label": "getUserMenuLinks$", + "description": [ + "\nReturns an Observable of the array of user menu links (the links that show up under the user's Avatar in the UI) registered by other plugins" + ], + "signature": [ + "() => ", + "Observable", + "<", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserMenuLink", + "text": "UserMenuLink" + }, + "[]>" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityNavControlServiceStart.addUserMenuLinks", + "type": "Function", + "tags": [], + "label": "addUserMenuLinks", + "description": [ + "\nRegisters the provided user menu links to be displayed in the user menu (the links that show up under the user's Avatar in the UI)." + ], + "signature": [ + "(newUserMenuLink: ", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserMenuLink", + "text": "UserMenuLink" + }, + "[]) => void" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityNavControlServiceStart.addUserMenuLinks.$1", + "type": "Array", + "tags": [], + "label": "newUserMenuLink", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserMenuLink", + "text": "UserMenuLink" + }, + "[]" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginSetup", + "type": "Interface", + "tags": [], + "label": "SecurityPluginSetup", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginSetup.authc", + "type": "Object", + "tags": [], + "label": "authc", + "description": [ + "\nExposes authentication information about the currently logged in user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.AuthenticationServiceSetup", + "text": "AuthenticationServiceSetup" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginSetup.license", + "type": "Object", + "tags": [], + "label": "license", + "description": [ + "\nExposes information about the available security features under the current license." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.SecurityLicense", + "text": "SecurityLicense" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginStart", + "type": "Interface", + "tags": [], + "label": "SecurityPluginStart", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginStart.navControlService", + "type": "Object", + "tags": [], + "label": "navControlService", + "description": [ + "\nExposes the ability to add custom links to the dropdown menu in the top right, where the user's Avatar is." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.SecurityNavControlServiceStart", + "text": "SecurityNavControlServiceStart" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginStart.authc", + "type": "Object", + "tags": [], + "label": "authc", + "description": [ + "\nExposes authentication information about the currently logged in user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.AuthenticationServiceSetup", + "text": "AuthenticationServiceSetup" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.SecurityPluginStart.userProfiles", + "type": "Object", + "tags": [], + "label": "userProfiles", + "description": [ + "\nA set of methods to work with Kibana user profiles." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileAPIClient", + "text": "UserProfileAPIClient" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink", + "type": "Interface", + "tags": [], + "label": "UserMenuLink", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.iconType", + "type": "CompoundType", + "tags": [], + "label": "iconType", + "description": [], + "signature": [ + "string | React.ComponentType<{}>" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.href", + "type": "string", + "tags": [], + "label": "href", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.setAsProfile", + "type": "CompoundType", + "tags": [], + "label": "setAsProfile", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserMenuLink.content", + "type": "CompoundType", + "tags": [], + "label": "content", + "description": [ + "Render a custom ReactNode instead of the default " + ], + "signature": [ + "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient", + "type": "Interface", + "tags": [], + "label": "UserProfileAPIClient", + "description": [], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.userProfile$", + "type": "Object", + "tags": [], + "label": "userProfile$", + "description": [], + "signature": [ + "Observable", + "<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileData", + "text": "UserProfileData" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.getCurrent", + "type": "Function", + "tags": [], + "label": "getCurrent", + "description": [ + "\nRetrieves the user profile of the current user. If the profile isn't available, e.g. for the anonymous users or\nusers authenticated via authenticating proxies, the `null` value is returned." + ], + "signature": [ + "(params?: ", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileGetCurrentParams", + "text": "UserProfileGetCurrentParams" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.GetUserProfileResponse", + "text": "GetUserProfileResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.getCurrent.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Get current user profile operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileGetCurrentParams", + "text": "UserProfileGetCurrentParams" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.bulkGet", + "type": "Function", + "tags": [], + "label": "bulkGet", + "description": [ + "\nRetrieves multiple user profiles by their identifiers." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileBulkGetParams", + "text": "UserProfileBulkGetParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "[]>" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.bulkGet.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Bulk get operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileBulkGetParams", + "text": "UserProfileBulkGetParams" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.suggest", + "type": "Function", + "tags": [], + "label": "suggest", + "description": [ + "\nSuggests multiple user profiles by search criteria.\n\nNote: This endpoint is not provided out-of-the-box by the platform. You need to expose your own\nversion within your app. An example of how to do this can be found in:\n`examples/user_profile_examples/server/plugin.ts`\n" + ], + "signature": [ + "(path: string, params: ", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileSuggestParams", + "text": "UserProfileSuggestParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "[]>" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.suggest.$1", + "type": "string", + "tags": [], + "label": "path", + "description": [ + "Path to your app's suggest endpoint." + ], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.suggest.$2", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Suggest operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.UserProfileSuggestParams", + "text": "UserProfileSuggestParams" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nUpdates user profile data of the current user." + ], + "signature": [ + "(data: D) => Promise" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileAPIClient.update.$1", + "type": "Uncategorized", + "tags": [], + "label": "data", + "description": [ + "Application data to be written (merged with existing data)." + ], + "signature": [ + "D" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileBulkGetParams", + "type": "Interface", + "tags": [], + "label": "UserProfileBulkGetParams", + "description": [ + "\nParameters for the bulk get API." + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileBulkGetParams.uids", + "type": "Object", + "tags": [], + "label": "uids", + "description": [ + "\nList of user profile identifiers." + ], + "signature": [ + "Set" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileBulkGetParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, suggest API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileGetCurrentParams", + "type": "Interface", + "tags": [], + "label": "UserProfileGetCurrentParams", + "description": [ + "\nParameters for the get user profile for the current user API." + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileGetCurrentParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, get API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileSuggestParams", + "type": "Interface", + "tags": [], + "label": "UserProfileSuggestParams", + "description": [ + "\nParameters for the suggest API." + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileSuggestParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nQuery string used to match name-related fields in user profiles. The following fields are treated as\nname-related: username, full_name and email." + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileSuggestParams.size", + "type": "number", + "tags": [], + "label": "size", + "description": [ + "\nDesired number of suggestions to return. The default value is 10." + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.UserProfileSuggestParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, suggest API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/security-plugin-types-public", + "id": "def-public.AuthenticationServiceStart", + "type": "Type", + "tags": [], + "label": "AuthenticationServiceStart", + "description": [ + "\nStart has the same contract as Setup for now." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.AuthenticationServiceSetup", + "text": "AuthenticationServiceSetup" + } + ], + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx new file mode 100644 index 0000000000000..265114bb7b6d0 --- /dev/null +++ b/api_docs/kbn_security_plugin_types_public.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: kibKbnSecurityPluginTypesPublicPluginApi +slug: /kibana-dev-docs/api/kbn-security-plugin-types-public +title: "@kbn/security-plugin-types-public" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/security-plugin-types-public plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] +--- +import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; + + + +Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 44 | 0 | 14 | 0 | + +## Client + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_security_plugin_types_server.devdocs.json b/api_docs/kbn_security_plugin_types_server.devdocs.json new file mode 100644 index 0000000000000..c6d441e7a04a7 --- /dev/null +++ b/api_docs/kbn_security_plugin_types_server.devdocs.json @@ -0,0 +1,4401 @@ +{ + "id": "@kbn/security-plugin-types-server", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.getKibanaRoleSchema", + "type": "Function", + "tags": [], + "label": "getKibanaRoleSchema", + "description": [ + "\nKibana specific portion of the role definition. It's represented as a list of base and/or\nfeature Kibana privileges. None of the entries should apply to the same spaces." + ], + "signature": [ + "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined; } & { spaces: string[] | \"*\"[]; }>[]>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.getKibanaRoleSchema.$1", + "type": "Function", + "tags": [], + "label": "getBasePrivilegeNames", + "description": [], + "signature": [ + "() => { global: string[]; space: string[]; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.getRestApiKeyWithKibanaPrivilegesSchema", + "type": "Function", + "tags": [], + "label": "getRestApiKeyWithKibanaPrivilegesSchema", + "description": [], + "signature": [ + "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ExtendedObjectType<{ type: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "<\"rest\" | undefined>; name: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; expiration: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; role_descriptors: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + ">>; metadata: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined>; }, { role_descriptors: null; kibana_role_descriptors: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>>; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.getRestApiKeyWithKibanaPrivilegesSchema.$1", + "type": "Function", + "tags": [], + "label": "getBasePrivilegeNames", + "description": [], + "signature": [ + "() => { global: string[]; space: string[]; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions", + "type": "Interface", + "tags": [], + "label": "Actions", + "description": [ + "Actions are used to create the \"actions\" that are associated with Elasticsearch's\napplication privileges, and are used to perform the authorization checks implemented\nby the various `checkPrivilegesWithRequest` derivatives." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.api", + "type": "Object", + "tags": [], + "label": "api", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ApiActions", + "text": "ApiActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.app", + "type": "Object", + "tags": [], + "label": "app", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AppActions", + "text": "AppActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.cases", + "type": "Object", + "tags": [], + "label": "cases", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CasesActions", + "text": "CasesActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.login", + "type": "string", + "tags": [], + "label": "login", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.savedObject", + "type": "Object", + "tags": [], + "label": "savedObject", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.SavedObjectActions", + "text": "SavedObjectActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.alerting", + "type": "Object", + "tags": [], + "label": "alerting", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AlertingActions", + "text": "AlertingActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.space", + "type": "Object", + "tags": [], + "label": "space", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.SpaceActions", + "text": "SpaceActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.Actions.ui", + "type": "Object", + "tags": [], + "label": "ui", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UIActions", + "text": "UIActions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions", + "type": "Interface", + "tags": [], + "label": "AlertingActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(ruleTypeId: string, consumer: string, alertingEntity: string, operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions.get.$1", + "type": "string", + "tags": [], + "label": "ruleTypeId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions.get.$2", + "type": "string", + "tags": [], + "label": "consumer", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions.get.$3", + "type": "string", + "tags": [], + "label": "alertingEntity", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AlertingActions.get.$4", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ApiActions", + "type": "Interface", + "tags": [], + "label": "ApiActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ApiActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ApiActions.get.$1", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys", + "type": "Interface", + "tags": [], + "label": "APIKeys", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.areAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areAPIKeysEnabled", + "description": [ + "\nDetermines if API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.areCrossClusterAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areCrossClusterAPIKeysEnabled", + "description": [ + "\nDetermines if Cross-Cluster API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nTries to create an API key for the current user.\n\nReturns newly created API key or `null` if API keys are disabled.\n\nUser needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + }, + ") => Promise<", + "SecurityCreateApiKeyResponse", + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "The params to create an API key" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser", + "type": "Function", + "tags": [], + "label": "grantAsInternalUser", + "description": [ + "\nTries to grant an API key for the current user." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.GrantAPIKeyResult", + "text": "GrantAPIKeyResult" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "Create operation parameters." + ], + "signature": [ + "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [ + "\nTries to validate an API key." + ], + "signature": [ + "(apiKeyPrams: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + }, + ") => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.validate.$1", + "type": "Object", + "tags": [], + "label": "apiKeyPrams", + "description": [ + "ValidateAPIKeyParams." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate", + "type": "Function", + "tags": [], + "label": "invalidate", + "description": [ + "\nTries to invalidate an API keys." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate.$2", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate an API keys." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidateAsInternalUser", + "type": "Function", + "tags": [], + "label": "invalidateAsInternalUser", + "description": [ + "\nTries to invalidate the API keys by using the internal user." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidateAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate the API keys." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions", + "type": "Interface", + "tags": [], + "label": "AppActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions.get.$1", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditEvent", + "type": "Interface", + "tags": [], + "label": "AuditEvent", + "description": [ + "\nAudit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html\n\nIf you add additional fields to the schema ensure you update the Kibana Filebeat module:\nhttps://github.com/elastic/beats/tree/master/filebeat/module/kibana\n" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " extends ", + { + "pluginId": "@kbn/logging", + "scope": "common", + "docId": "kibKbnLoggingPluginApi", + "section": "def-common.LogMeta", + "text": "LogMeta" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditEvent.message", + "type": "string", + "tags": [], + "label": "message", + "description": [ + "\nLog message" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditEvent.kibana", + "type": "Object", + "tags": [], + "label": "kibana", + "description": [ + "\nKibana specific fields" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditKibana", + "text": "AuditKibana" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditEvent.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [ + "\nFields describing an HTTP request" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditHttp", + "text": "AuditHttp" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditHttp", + "type": "Interface", + "tags": [], + "label": "AuditHttp", + "description": [ + "\nAudit http schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditHttp", + "text": "AuditHttp" + }, + " extends ", + { + "pluginId": "@kbn/ecs", + "scope": "common", + "docId": "kibKbnEcsPluginApi", + "section": "def-common.EcsHttp", + "text": "EcsHttp" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditHttp.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "\nHTTP request details" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditRequest", + "text": "AuditRequest" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana", + "type": "Interface", + "tags": [], + "label": "AuditKibana", + "description": [ + "\nAudit kibana schema using ECS format" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.space_id", + "type": "string", + "tags": [], + "label": "space_id", + "description": [ + "\nThe ID of the space associated with this event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.session_id", + "type": "string", + "tags": [], + "label": "session_id", + "description": [ + "\nThe ID of the user session associated with this event. Each login attempt\nresults in a unique session id." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.saved_object", + "type": "Object", + "tags": [], + "label": "saved_object", + "description": [ + "\nSaved object that was created, changed, deleted or accessed as part of this event." + ], + "signature": [ + "{ type: string; id: string; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.authentication_provider", + "type": "string", + "tags": [], + "label": "authentication_provider", + "description": [ + "\nName of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.authentication_type", + "type": "string", + "tags": [], + "label": "authentication_type", + "description": [ + "\nType of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.authentication_realm", + "type": "string", + "tags": [], + "label": "authentication_realm", + "description": [ + "\nName of Elasticsearch realm that has authenticated the user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.lookup_realm", + "type": "string", + "tags": [], + "label": "lookup_realm", + "description": [ + "\nName of Elasticsearch realm where the user details were retrieved from." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.add_to_spaces", + "type": "Object", + "tags": [], + "label": "add_to_spaces", + "description": [ + "\nSet of space IDs that a saved object was shared to." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.delete_from_spaces", + "type": "Object", + "tags": [], + "label": "delete_from_spaces", + "description": [ + "\nSet of space IDs that a saved object was removed from." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.unauthorized_spaces", + "type": "Object", + "tags": [], + "label": "unauthorized_spaces", + "description": [ + "\nSet of space IDs that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditKibana.unauthorized_types", + "type": "Object", + "tags": [], + "label": "unauthorized_types", + "description": [ + "\nSet of types that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditLogger", + "type": "Interface", + "tags": [], + "label": "AuditLogger", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditLogger.log", + "type": "Function", + "tags": [], + "label": "log", + "description": [ + "\nLogs an {@link AuditEvent} and automatically adds meta data about the\ncurrent user, space and correlation id.\n\nGuidelines around what events should be logged and how they should be\nstructured can be found in: `/x-pack/plugins/security/README.md`\n" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " | undefined) => void" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditLogger.log.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditLogger.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nIndicates whether audit logging is enabled or not.\n\nUseful for skipping resource-intense operations that don't need to be performed when audit\nlogging is disabled." + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditRequest", + "type": "Interface", + "tags": [], + "label": "AuditRequest", + "description": [ + "\nAudit request schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditRequest", + "text": "AuditRequest" + }, + " extends { body?: { bytes?: number | undefined; content?: string | undefined; } | undefined; bytes?: number | undefined; id?: string | undefined; method?: string | undefined; mime_type?: string | undefined; referrer?: string | undefined; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditRequest.headers", + "type": "Object", + "tags": [], + "label": "headers", + "description": [ + "\nHTTP request headers" + ], + "signature": [ + "{ 'x-forwarded-for'?: string | undefined; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditServiceSetup", + "type": "Interface", + "tags": [], + "label": "AuditServiceSetup", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditServiceSetup.asScoped", + "type": "Function", + "tags": [], + "label": "asScoped", + "description": [ + "\nCreates an {@link AuditLogger} scoped to the current request.\n\nThis audit logger logs events with all required user and session info and should be used for\nall user-initiated actions.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditServiceSetup.asScoped.$1", + "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/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuditServiceSetup.withoutRequest", + "type": "Object", + "tags": [], + "label": "withoutRequest", + "description": [ + "\n{@link AuditLogger} for background tasks only.\n\nThis audit logger logs events without any user or session info and should never be used to log\nuser-initiated actions.\n" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthenticationServiceStart", + "type": "Interface", + "tags": [], + "label": "AuthenticationServiceStart", + "description": [ + "\nAuthentication services available on the security plugin's start contract." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthenticationServiceStart.apiKeys", + "type": "Object", + "tags": [], + "label": "apiKeys", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.APIKeys", + "text": "APIKeys" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthenticationServiceStart.getCurrentUser", + "type": "Function", + "tags": [], + "label": "getCurrentUser", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthenticationServiceStart.getCurrentUser.$1", + "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/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationMode", + "type": "Interface", + "tags": [], + "label": "AuthorizationMode", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationMode.useRbacForRequest", + "type": "Function", + "tags": [], + "label": "useRbacForRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => boolean" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationMode.useRbacForRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup", + "type": "Interface", + "tags": [], + "label": "AuthorizationServiceSetup", + "description": [ + "\nAuthorization services available on the setup contract of the security plugin." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [ + "\nActions are used to create the \"actions\" that are associated with Elasticsearch's\napplication privileges, and are used to perform the authorization checks implemented\nby the various `checkPrivilegesWithRequest` derivatives." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.Actions", + "text": "Actions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesWithRequest", + "type": "Function", + "tags": [], + "label": "checkPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivileges", + "text": "CheckPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesDynamicallyWithRequest", + "type": "Function", + "tags": [], + "label": "checkPrivilegesDynamicallyWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesDynamically", + "text": "CheckPrivilegesDynamically" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesDynamicallyWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkSavedObjectsPrivilegesWithRequest", + "type": "Function", + "tags": [], + "label": "checkSavedObjectsPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckSavedObjectsPrivileges", + "text": "CheckSavedObjectsPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.checkSavedObjectsPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AuthorizationServiceSetup.mode", + "type": "Object", + "tags": [], + "label": "mode", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuthorizationMode", + "text": "AuthorizationMode" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CasesActions", + "type": "Interface", + "tags": [], + "label": "CasesActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CasesActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(owner: string, operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CasesActions.get.$1", + "type": "string", + "tags": [], + "label": "owner", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CasesActions.get.$2", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges", + "type": "Interface", + "tags": [], + "label": "CheckPrivileges", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpace", + "type": "Function", + "tags": [], + "label": "atSpace", + "description": [], + "signature": [ + "(spaceId: string, privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpace.$1", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpace.$2", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpace.$3", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpaces", + "type": "Function", + "tags": [], + "label": "atSpaces", + "description": [], + "signature": [ + "(spaceIds: string[], privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpaces.$1", + "type": "Array", + "tags": [], + "label": "spaceIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpaces.$2", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.atSpaces.$3", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.globally", + "type": "Function", + "tags": [], + "label": "globally", + "description": [], + "signature": [ + "(privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.globally.$1", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.globally.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesOptions", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesOptions", + "description": [ + "\nOptions to influce the privilege checks." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesOptions.requireLoginAction", + "type": "CompoundType", + "tags": [], + "label": "requireLoginAction", + "description": [ + "\nWhether or not the `login` action should be required (default: true).\nSetting this to false is not advised except for special circumstances, when you do not require\nthe request to belong to a user capable of logging into Kibana." + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesPayload", + "description": [ + "\nPrivileges that can be checked for the Kibana users." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload.kibana", + "type": "CompoundType", + "tags": [], + "label": "kibana", + "description": [ + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." + ], + "signature": [ + "string | string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [ + "\nA set of the Elasticsearch cluster and index privileges." + ], + "signature": [ + "{ cluster: string[]; index: Record; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.hasAllRequested", + "type": "boolean", + "tags": [], + "label": "hasAllRequested", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.privileges", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + "{ kibana: { resource?: string | undefined; privilege: string; authorized: boolean; }[]; elasticsearch: { cluster: { privilege: string; authorized: boolean; }[]; index: { [indexName: string]: { privilege: string; authorized: boolean; }[]; }; }; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivileges", + "description": [ + "\nAn interface to check users profiles privileges in a specific context (only a single-space context is supported at\nthe moment)." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges.atSpace", + "type": "Function", + "tags": [], + "label": "atSpace", + "description": [], + "signature": [ + "(spaceId: string, privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesResponse", + "text": "CheckUserProfilesPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$1", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$2", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesPayload", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivilegesPayload", + "description": [ + "\nPrivileges that can be checked for the users profiles (only Kibana specific privileges are supported at the moment)." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesPayload.kibana", + "type": "Array", + "tags": [], + "label": "kibana", + "description": [ + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivilegesResponse", + "description": [ + "\nResponse of the check privileges operation for the users profiles." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse.hasPrivilegeUids", + "type": "Array", + "tags": [], + "label": "hasPrivilegeUids", + "description": [ + "\nThe subset of the requested profile IDs of the users that have all the requested privileges." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse.errors", + "type": "Object", + "tags": [], + "label": "errors", + "description": [ + "\nAn errors object that may be returned from ES that contains a `count` of UIDs that have errors in the `details` property.\n\nEach entry in `details` will contain an error `type`, e.g 'resource_not_found_exception', and a `reason` message, e.g. 'profile document not found'" + ], + "signature": [ + "{ count: number; details: Record; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.GrantAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "GrantAPIKeyResult", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.GrantAPIKeyResult.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique id for this API key" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.GrantAPIKeyResult.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nName for this API key" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.GrantAPIKeyResult.api_key", + "type": "string", + "tags": [], + "label": "api_key", + "description": [ + "\nGenerated API key" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "HasPrivilegesResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse.has_all_requested", + "type": "boolean", + "tags": [], + "label": "has_all_requested", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse.application", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + "{ [applicationName: string]: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.HasPrivilegesResponseApplication", + "text": "HasPrivilegesResponseApplication" + }, + "; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse.cluster", + "type": "Object", + "tags": [], + "label": "cluster", + "description": [], + "signature": [ + "{ [privilegeName: string]: boolean; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponse.index", + "type": "Object", + "tags": [], + "label": "index", + "description": [], + "signature": [ + "{ [indexName: string]: { [privilegeName: string]: boolean; }; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponseApplication", + "type": "Interface", + "tags": [], + "label": "HasPrivilegesResponseApplication", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.HasPrivilegesResponseApplication.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[resource: string]: { [privilegeName: string]: boolean; }", + "description": [], + "signature": [ + "[resource: string]: { [privilegeName: string]: boolean; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "InvalidateAPIKeyResult", + "description": [ + "\nThe return value when invalidating an API key in Elasticsearch." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeyResult.invalidated_api_keys", + "type": "Array", + "tags": [], + "label": "invalidated_api_keys", + "description": [ + "\nThe IDs of the API keys that were invalidated as part of the request." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeyResult.previously_invalidated_api_keys", + "type": "Array", + "tags": [], + "label": "previously_invalidated_api_keys", + "description": [ + "\nThe IDs of the API keys that were already invalidated." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeyResult.error_count", + "type": "number", + "tags": [], + "label": "error_count", + "description": [ + "\nThe number of errors that were encountered when invalidating the API keys." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeyResult.error_details", + "type": "Array", + "tags": [], + "label": "error_details", + "description": [ + "\nDetails about these errors. This field is not present in the response when error_count is 0." + ], + "signature": [ + "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeysParams", + "type": "Interface", + "tags": [], + "label": "InvalidateAPIKeysParams", + "description": [ + "\nRepresents the params for invalidating multiple API keys" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.InvalidateAPIKeysParams.ids", + "type": "Array", + "tags": [], + "label": "ids", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsRolesByFeatureIdRequest", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest.context", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-deprecations-server", + "scope": "common", + "docId": "kibKbnCoreDeprecationsServerPluginApi", + "section": "def-common.GetDeprecationsContext", + "text": "GetDeprecationsContext" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest.featureId", + "type": "string", + "tags": [], + "label": "featureId", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsRolesByFeatureIdResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse.roles", + "type": "Array", + "tags": [], + "label": "roles", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.Role", + "text": "Role" + }, + "[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse.errors", + "type": "Array", + "tags": [], + "label": "errors", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-deprecations-common", + "scope": "common", + "docId": "kibKbnCoreDeprecationsCommonPluginApi", + "section": "def-common.DeprecationsDetails", + "text": "DeprecationsDetails" + }, + "[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsService", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsService", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsService.getKibanaRolesByFeatureId", + "type": "Function", + "tags": [], + "label": "getKibanaRolesByFeatureId", + "description": [], + "signature": [ + "(args: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "text": "PrivilegeDeprecationsRolesByFeatureIdRequest" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse", + "text": "PrivilegeDeprecationsRolesByFeatureIdResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.PrivilegeDeprecationsService.getKibanaRolesByFeatureId.$1", + "type": "Object", + "tags": [], + "label": "args", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "text": "PrivilegeDeprecationsRolesByFeatureIdRequest" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SavedObjectActions", + "type": "Interface", + "tags": [], + "label": "SavedObjectActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SavedObjectActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(type: string, operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SavedObjectActions.get.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SavedObjectActions.get.$2", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginSetup", + "type": "Interface", + "tags": [], + "label": "SecurityPluginSetup", + "description": [ + "\nDescribes public Security plugin contract returned at the `setup` stage." + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginSetup.license", + "type": "Object", + "tags": [], + "label": "license", + "description": [ + "\nExposes information about the available security features under the current license." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.SecurityLicense", + "text": "SecurityLicense" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginSetup.audit", + "type": "Object", + "tags": [], + "label": "audit", + "description": [ + "\nExposes services for audit logging." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditServiceSetup", + "text": "AuditServiceSetup" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginSetup.privilegeDeprecationsService", + "type": "Object", + "tags": [], + "label": "privilegeDeprecationsService", + "description": [ + "\nExposes services to access kibana roles per feature id with the GetDeprecationsContext" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsService", + "text": "PrivilegeDeprecationsService" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginStart", + "type": "Interface", + "tags": [], + "label": "SecurityPluginStart", + "description": [ + "\nDescribes public Security plugin contract returned at the `start` stage." + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginStart.authc", + "type": "Object", + "tags": [], + "label": "authc", + "description": [ + "\nAuthentication services to confirm the user is who they say they are." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuthenticationServiceStart", + "text": "AuthenticationServiceStart" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginStart.authz", + "type": "Object", + "tags": [], + "label": "authz", + "description": [ + "\nAuthorization services to manage and access the permissions a particular user has." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuthorizationServiceSetup", + "text": "AuthorizationServiceSetup" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SecurityPluginStart.userProfiles", + "type": "Object", + "tags": [], + "label": "userProfiles", + "description": [ + "\nUser profiles services to retrieve user profiles." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileServiceStart", + "text": "UserProfileServiceStart" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SpaceActions", + "type": "Interface", + "tags": [], + "label": "SpaceActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.SpaceActions.manage", + "type": "string", + "tags": [], + "label": "manage", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions", + "type": "Interface", + "tags": [], + "label": "UIActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(featureId: keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + }, + ", ...uiCapabilityParts: string[]) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions.get.$1", + "type": "CompoundType", + "tags": [], + "label": "featureId", + "description": [], + "signature": [ + "keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions.get.$2", + "type": "Array", + "tags": [], + "label": "uiCapabilityParts", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileBulkGetParams", + "type": "Interface", + "tags": [], + "label": "UserProfileBulkGetParams", + "description": [ + "\nParameters for the bulk get API." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileBulkGetParams.uids", + "type": "Object", + "tags": [], + "label": "uids", + "description": [ + "\nList of user profile identifiers." + ], + "signature": [ + "Set" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileBulkGetParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, suggest API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileGetCurrentParams", + "type": "Interface", + "tags": [], + "label": "UserProfileGetCurrentParams", + "description": [ + "\nParameters for the get user profile for the current user API." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileGetCurrentParams.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "\nUser request instance to get user profile for." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileGetCurrentParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, get API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileRequiredPrivileges", + "type": "Interface", + "tags": [], + "label": "UserProfileRequiredPrivileges", + "description": [ + "\nThe set of privileges that users associated with the suggested user profile should have for a specified space id." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileRequiredPrivileges.spaceId", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [ + "\nThe id of the Kibana Space." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileRequiredPrivileges.privileges", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [ + "\nThe set of the Kibana specific application privileges." + ], + "signature": [ + "{ kibana: string[]; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart", + "type": "Interface", + "tags": [], + "label": "UserProfileServiceStart", + "description": [ + "\nA set of methods to work with Kibana user profiles." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.getCurrent", + "type": "Function", + "tags": [], + "label": "getCurrent", + "description": [ + "\nRetrieves a user profile for the current user extracted from the specified request. If the profile isn't available,\ne.g. for the anonymous users or users authenticated via authenticating proxies, the `null` value is returned." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileGetCurrentParams", + "text": "UserProfileGetCurrentParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfileWithSecurity", + "text": "UserProfileWithSecurity" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.getCurrent.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Get current user profile operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileGetCurrentParams", + "text": "UserProfileGetCurrentParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.bulkGet", + "type": "Function", + "tags": [], + "label": "bulkGet", + "description": [ + "\nRetrieves multiple user profiles by their identifiers." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileBulkGetParams", + "text": "UserProfileBulkGetParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "[]>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.bulkGet.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Bulk get operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileBulkGetParams", + "text": "UserProfileBulkGetParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.suggest", + "type": "Function", + "tags": [], + "label": "suggest", + "description": [ + "\nSuggests multiple user profiles by search criteria." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileSuggestParams", + "text": "UserProfileSuggestParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserProfile", + "text": "UserProfile" + }, + "[]>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileServiceStart.suggest.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Suggest operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileSuggestParams", + "text": "UserProfileSuggestParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams", + "type": "Interface", + "tags": [], + "label": "UserProfileSuggestParams", + "description": [ + "\nParameters for the suggest API." + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nQuery string used to match name-related fields in user profiles. The following fields are treated as\nname-related: username, full_name and email." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams.hint", + "type": "Object", + "tags": [], + "label": "hint", + "description": [ + "\nExtra search criteria to improve relevance of the suggestion result. A profile matching the\nspecified hint is ranked higher in the response. But not-matching the hint does not exclude a\nprofile from the response as long as it matches the `name` field query." + ], + "signature": [ + "{ uids: string[]; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams.size", + "type": "number", + "tags": [], + "label": "size", + "description": [ + "\nDesired number of suggestion to return. The default value is 10." + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams.dataPath", + "type": "string", + "tags": [], + "label": "dataPath", + "description": [ + "\nBy default, suggest API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UserProfileSuggestParams.requiredPrivileges", + "type": "Object", + "tags": [], + "label": "requiredPrivileges", + "description": [ + "\nThe set of the privileges that users associated with the suggested user profile should have in the specified space.\nIf not specified, privileges check isn't performed and all matched profiles are returned irrespective to the\nprivileges of the associated users." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileRequiredPrivileges", + "text": "UserProfileRequiredPrivileges" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ValidateAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "ValidateAPIKeyParams", + "description": [ + "\nRepresents the parameters for validating API Key credentials." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ValidateAPIKeyParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique id for this API key" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ValidateAPIKeyParams.api_key", + "type": "string", + "tags": [], + "label": "api_key", + "description": [ + "\nGenerated API Key (secret)" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesDynamically", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesDynamically", + "description": [], + "signature": [ + "(privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesDynamically.$1", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesDynamically.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesDynamicallyWithRequest", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesDynamicallyWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesDynamically", + "text": "CheckPrivilegesDynamically" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesDynamicallyWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesWithRequest", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivileges", + "text": "CheckPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckSavedObjectsPrivileges", + "type": "Type", + "tags": [], + "label": "CheckSavedObjectsPrivileges", + "description": [], + "signature": [ + "(actions: string | string[], namespaceOrNamespaces?: string | (string | undefined)[] | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckSavedObjectsPrivileges.$1", + "type": "CompoundType", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "string | string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckSavedObjectsPrivileges.$2", + "type": "CompoundType", + "tags": [], + "label": "namespaceOrNamespaces", + "description": [], + "signature": [ + "string | (string | undefined)[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckSavedObjectsPrivilegesWithRequest", + "type": "Type", + "tags": [], + "label": "CheckSavedObjectsPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckSavedObjectsPrivileges", + "text": "CheckSavedObjectsPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckSavedObjectsPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateAPIKeyParams", + "type": "Type", + "tags": [], + "label": "CreateAPIKeyParams", + "description": [], + "signature": [ + "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { type: \"cross_cluster\"; name: string; access: Readonly<{ search?: Readonly<{} & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateAPIKeyResult", + "type": "Type", + "tags": [], + "label": "CreateAPIKeyResult", + "description": [ + "\nResponse of Kibana Create API key endpoint." + ], + "signature": [ + "SecurityCreateApiKeyResponse" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams", + "type": "Type", + "tags": [], + "label": "CreateCrossClusterAPIKeyParams", + "description": [], + "signature": [ + "{ readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly type: \"cross_cluster\"; readonly name: string; readonly access: Readonly<{ search?: Readonly<{} & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyParams", + "type": "Type", + "tags": [], + "label": "CreateRestAPIKeyParams", + "description": [], + "signature": [ + "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly role_descriptors: Record>; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams", + "type": "Type", + "tags": [], + "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", + "description": [], + "signature": [ + "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ElasticsearchPrivilegesType", + "type": "Type", + "tags": [], + "label": "ElasticsearchPrivilegesType", + "description": [], + "signature": [ + "{ readonly cluster?: string[] | undefined; readonly indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; readonly remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; readonly run_as?: string[] | undefined; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.GLOBAL_RESOURCE", + "type": "string", + "tags": [], + "label": "GLOBAL_RESOURCE", + "description": [], + "signature": [ + "\"*\"" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.KibanaPrivilegesType", + "type": "Type", + "tags": [], + "label": "KibanaPrivilegesType", + "description": [], + "signature": [ + "Readonly<{ base?: string[] | undefined; feature?: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.crossClusterApiKeySchema", + "type": "Object", + "tags": [], + "label": "crossClusterApiKeySchema", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ObjectType", + "text": "ObjectType" + }, + "; name: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; expiration: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; role_descriptors: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + ">>; metadata: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined>; }, { type: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "<\"cross_cluster\">; role_descriptors: null; access: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ObjectType", + "text": "ObjectType" + }, + "<{ search: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "[] | undefined>; replication: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "[] | undefined>; }>; }>>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.elasticsearchRoleSchema", + "type": "Object", + "tags": [], + "label": "elasticsearchRoleSchema", + "description": [ + "\nElasticsearch specific portion of the role definition.\nSee more details at https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api.html#security-role-apis." + ], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ObjectType", + "text": "ObjectType" + }, + "<{ cluster: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; indices: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined>; remote_indices: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined>; run_as: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.restApiKeySchema", + "type": "Object", + "tags": [], + "label": "restApiKeySchema", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.ObjectType", + "text": "ObjectType" + }, + "<{ type: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "<\"rest\" | undefined>; name: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; expiration: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "; role_descriptors: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + ">>; metadata: ", + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + " | undefined>; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx new file mode 100644 index 0000000000000..3ca91debf2962 --- /dev/null +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSecurityPluginTypesServerPluginApi +slug: /kibana-dev-docs/api/kbn-security-plugin-types-server +title: "@kbn/security-plugin-types-server" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/security-plugin-types-server plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] +--- +import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; + + + +Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 213 | 0 | 114 | 0 | + +## Server + +### Objects + + +### Functions + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_security_solution_features.devdocs.json b/api_docs/kbn_security_solution_features.devdocs.json index efeb01d3237a1..ec6f97556d3cc 100644 --- a/api_docs/kbn_security_solution_features.devdocs.json +++ b/api_docs/kbn_security_solution_features.devdocs.json @@ -418,7 +418,7 @@ "section": "def-common.RecursivePartial", "text": "RecursivePartial" }, - "<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; } | undefined>; disabled?: ", + "<{ all?: readonly string[] | undefined; push?: readonly string[] | undefined; create?: readonly string[] | undefined; read?: readonly string[] | undefined; update?: readonly string[] | undefined; delete?: readonly string[] | undefined; settings?: readonly string[] | undefined; } | undefined>; disabled?: ", { "pluginId": "@kbn/utility-types", "scope": "common", diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index c04fe62254143..6734304de4b08 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 487d41792eaf6..43a75a27bcb3d 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 4e41338839148..43997bd610293 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 6620995af5f3f..370cb7d79f10d 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 8e74787f39f15..0e36b5dcc82df 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 0aebebe122853..fa04474abf9a3 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index f1073f93a3ccb..eb1cb46194c85 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-11-22 +date: 2023-11-30 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 0173f985f32f7..88a7c3baaec79 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json index 09a5e5bbddaff..43eab9a3b5012 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.devdocs.json +++ b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json @@ -834,7 +834,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"section\" | \"circle\" | \"code\" | \"line\" | \"area\" | \"animate\" | \"view\" | \"var\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"br\" | \"clipPath\" | \"textarea\" | \"abbr\" | \"address\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -848,7 +848,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"section\" | \"circle\" | \"code\" | \"line\" | \"area\" | \"animate\" | \"view\" | \"var\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"br\" | \"clipPath\" | \"textarea\" | \"abbr\" | \"address\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -987,7 +987,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"section\" | \"circle\" | \"code\" | \"line\" | \"area\" | \"animate\" | \"view\" | \"var\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"br\" | \"clipPath\" | \"textarea\" | \"abbr\" | \"address\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, @@ -1001,7 +1001,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"section\" | \"circle\" | \"code\" | \"line\" | \"area\" | \"animate\" | \"view\" | \"var\" | \"html\" | \"a\" | \"img\" | \"audio\" | \"br\" | \"clipPath\" | \"textarea\" | \"abbr\" | \"address\" | \"aside\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"video\" | \"wbr\" | \"webview\" | \"animateMotion\" | \"animateTransform\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 0dba2f1fb8159..7995b7dc67dd9 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.devdocs.json b/api_docs/kbn_securitysolution_grouping.devdocs.json index 2e18758ff84be..86037516e7000 100644 --- a/api_docs/kbn_securitysolution_grouping.devdocs.json +++ b/api_docs/kbn_securitysolution_grouping.devdocs.json @@ -108,7 +108,9 @@ "\nHook to configure grouping component" ], "signature": [ - "({ componentProps, defaultGroupingOptions, fields, groupingId, maxGroupingLevels, onGroupChange, onOptionsChange, tracker, }: GroupingArgs) => Grouping" + "({ componentProps, defaultGroupingOptions, fields, groupingId, maxGroupingLevels, onGroupChange, onOptionsChange, tracker, title, }: GroupingArgs) => ", + "UseGrouping", + "" ], "path": "packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx", "deprecated": false, @@ -119,7 +121,7 @@ "id": "def-common.useGrouping.$1", "type": "Object", "tags": [], - "label": "{\n componentProps,\n defaultGroupingOptions,\n fields,\n groupingId,\n maxGroupingLevels,\n onGroupChange,\n onOptionsChange,\n tracker,\n}", + "label": "{\n componentProps,\n defaultGroupingOptions,\n fields,\n groupingId,\n maxGroupingLevels,\n onGroupChange,\n onOptionsChange,\n tracker,\n title,\n}", "description": [], "signature": [ "GroupingArgs" diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index ca5fb3fec2d44..1286e880af8dc 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elast | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 17 | 0 | 12 | 7 | +| 17 | 0 | 12 | 8 | ## Common diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index a7336a96af240..3ef05efaadd6a 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-11-22 +date: 2023-11-30 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 8f6d498c8bad5..025c0b636aa50 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-11-22 +date: 2023-11-30 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 779b89a3aa3bf..6bfb989cc5c99 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-11-22 +date: 2023-11-30 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 b5d56da4b664d..943279a65d589 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-11-22 +date: 2023-11-30 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 69d02a983f56b..4509118c02b1a 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-11-22 +date: 2023-11-30 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 7b46e125d2aba..675cc0fed06f1 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_securitysolution_list_constants.devdocs.json index 1c3812564f68e..04c226b657fcf 100644 --- a/api_docs/kbn_securitysolution_list_constants.devdocs.json +++ b/api_docs/kbn_securitysolution_list_constants.devdocs.json @@ -299,14 +299,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/event_filter_validator.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/event_filters/constants.ts" @@ -900,14 +892,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts" diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index a07e24cb27ec7..26c3eb9fd1a0d 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-11-22 +date: 2023-11-30 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 d30e825acaf63..693c3d2251d23 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-11-22 +date: 2023-11-30 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 b3dc08621fe26..25ed93c63bc3e 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-11-22 +date: 2023-11-30 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 4f46d389ace89..f1555e407499d 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-11-22 +date: 2023-11-30 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 11c2d81f27f49..83cdf36c0e836 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_securitysolution_utils.devdocs.json index 515eb8ddac528..50fb827a8705a 100644 --- a/api_docs/kbn_securitysolution_utils.devdocs.json +++ b/api_docs/kbn_securitysolution_utils.devdocs.json @@ -466,7 +466,7 @@ "section": "def-common.OperatingSystem", "text": "OperatingSystem" }, - "; value?: string | undefined; }) => string | undefined" + "; value: string; }) => string | undefined" ], "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", "deprecated": false, @@ -477,7 +477,7 @@ "id": "def-common.validateFilePathInput.$1", "type": "Object", "tags": [], - "label": "{\n os,\n value = '',\n}", + "label": "{\n os,\n value,\n}", "description": [], "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", "deprecated": false, @@ -510,6 +510,90 @@ "tags": [], "label": "value", "description": [], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validatePotentialWildcardInput", + "type": "Function", + "tags": [], + "label": "validatePotentialWildcardInput", + "description": [], + "signature": [ + "({ field, os, value, }: { field?: string | undefined; os: ", + { + "pluginId": "@kbn/securitysolution-utils", + "scope": "common", + "docId": "kibKbnSecuritysolutionUtilsPluginApi", + "section": "def-common.OperatingSystem", + "text": "OperatingSystem" + }, + "; value?: string | undefined; }) => string | undefined" + ], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validatePotentialWildcardInput.$1", + "type": "Object", + "tags": [], + "label": "{\n field = '',\n os,\n value = '',\n}", + "description": [], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validatePotentialWildcardInput.$1.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validatePotentialWildcardInput.$1.os", + "type": "Enum", + "tags": [], + "label": "os", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-utils", + "scope": "common", + "docId": "kibKbnSecuritysolutionUtilsPluginApi", + "section": "def-common.OperatingSystem", + "text": "OperatingSystem" + } + ], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validatePotentialWildcardInput.$1.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], "signature": [ "string | undefined" ], @@ -522,6 +606,39 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validateWildcardInput", + "type": "Function", + "tags": [], + "label": "validateWildcardInput", + "description": [], + "signature": [ + "(value?: string | undefined) => string | undefined" + ], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.validateWildcardInput.$1", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], @@ -624,18 +741,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/securitysolution-utils", - "id": "def-common.FILENAME_WILDCARD_WARNING", - "type": "string", - "tags": [], - "label": "FILENAME_WILDCARD_WARNING", - "description": [], - "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/securitysolution-utils", "id": "def-common.FILEPATH_WARNING", @@ -677,6 +782,18 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.WILDCARD_WARNING", + "type": "string", + "tags": [], + "label": "WILDCARD_WARNING", + "description": [], + "path": "packages/kbn-securitysolution-utils/src/path_validations/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 9d64d17ddde44..12168d8190fe7 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-detection-engine](https://github.com/orgs/elastic/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 37 | 0 | 32 | 0 | +| 44 | 0 | 39 | 0 | ## Common diff --git a/api_docs/kbn_server_http_tools.devdocs.json b/api_docs/kbn_server_http_tools.devdocs.json index ace0c332ca518..768f9868081a4 100644 --- a/api_docs/kbn_server_http_tools.devdocs.json +++ b/api_docs/kbn_server_http_tools.devdocs.json @@ -177,6 +177,52 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.SslConfig.isEqualTo", + "type": "Function", + "tags": [], + "label": "isEqualTo", + "description": [], + "signature": [ + "(otherConfig: ", + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.SslConfig", + "text": "SslConfig" + }, + ") => boolean" + ], + "path": "packages/kbn-server-http-tools/src/ssl/ssl_config.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.SslConfig.isEqualTo.$1", + "type": "Object", + "tags": [], + "label": "otherConfig", + "description": [], + "signature": [ + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.SslConfig", + "text": "SslConfig" + } + ], + "path": "packages/kbn-server-http-tools/src/ssl/ssl_config.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -492,6 +538,125 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.getServerTLSOptions", + "type": "Function", + "tags": [], + "label": "getServerTLSOptions", + "description": [ + "\nConverts Kibana `SslConfig` into `TLSOptions` that are accepted by the Hapi server,\nand by https.Server.setSecureContext()" + ], + "signature": [ + "(ssl: ", + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.ISslConfig", + "text": "ISslConfig" + }, + ") => ", + "ServerOptions", + " | undefined" + ], + "path": "packages/kbn-server-http-tools/src/get_server_options.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.getServerTLSOptions.$1", + "type": "Object", + "tags": [], + "label": "ssl", + "description": [], + "signature": [ + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.ISslConfig", + "text": "ISslConfig" + } + ], + "path": "packages/kbn-server-http-tools/src/get_server_options.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.setTlsConfig", + "type": "Function", + "tags": [], + "label": "setTlsConfig", + "description": [], + "signature": [ + "(hapiServer: ", + "Server", + ", sslConfig: ", + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.ISslConfig", + "text": "ISslConfig" + }, + ") => void" + ], + "path": "packages/kbn-server-http-tools/src/set_tls_config.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.setTlsConfig.$1", + "type": "Object", + "tags": [], + "label": "hapiServer", + "description": [], + "signature": [ + "Server" + ], + "path": "packages/kbn-server-http-tools/src/set_tls_config.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/server-http-tools", + "id": "def-common.setTlsConfig.$2", + "type": "Object", + "tags": [], + "label": "sslConfig", + "description": [], + "signature": [ + { + "pluginId": "@kbn/server-http-tools", + "scope": "common", + "docId": "kibKbnServerHttpToolsPluginApi", + "section": "def-common.ISslConfig", + "text": "ISslConfig" + } + ], + "path": "packages/kbn-server-http-tools/src/set_tls_config.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index daf652363885e..dae69389a62b0 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.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 | |-------------------|-----------|------------------------|-----------------| -| 54 | 0 | 51 | 1 | +| 61 | 0 | 57 | 1 | ## Common diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index b0575dbabfb7a..f1c3649f7ac20 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 94b3a1707196f..e3629a39e9115 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index 234b0e69db761..528a3d4c4b2b4 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 59247120561d5..bcba765b8b132 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 6e19923f7540c..e62db2ca40efd 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 4019e4dc561d8..6e51ed05a4451 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index ba1651ec89299..93c7bd4f514b2 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index c7faa33159304..aeeb34806f7a5 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-11-22 +date: 2023-11-30 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 84d5ed186bb7a..fcc1522198719 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 4c516c1fba5e5..da7c1593fd8ee 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 1ae22f39a08be..5b3c0ce9180d8 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.devdocs.json b/api_docs/kbn_shared_ux_card_no_data.devdocs.json index 30043835cff52..e5df01115951e 100644 --- a/api_docs/kbn_shared_ux_card_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_card_no_data.devdocs.json @@ -148,7 +148,7 @@ "signature": [ "Partial> & { button?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; description?: React.ReactNode; category?: string | undefined; canAccessFleet?: boolean | undefined; }" + ", \"description\" | \"onClick\" | \"isDisabled\" | \"button\" | \"layout\">> & { button?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; description?: React.ReactNode; category?: string | undefined; canAccessFleet?: boolean | undefined; }" ], "path": "packages/shared-ux/card/no_data/types/index.d.ts", "deprecated": false, @@ -186,7 +186,7 @@ "signature": [ "Partial> & { button?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; description?: React.ReactNode; category?: string | undefined; canAccessFleet?: boolean | undefined; }" + ", \"description\" | \"onClick\" | \"isDisabled\" | \"button\" | \"layout\">> & { button?: React.ReactNode; onClick?: React.MouseEventHandler | undefined; description?: React.ReactNode; category?: string | undefined; canAccessFleet?: boolean | undefined; }" ], "path": "packages/shared-ux/card/no_data/types/index.d.ts", "deprecated": false, diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 90b8d8e15f6e6..f2d81f1964003 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-11-22 +date: 2023-11-30 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 e7bff693dbb90..501e438769d68 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.devdocs.json b/api_docs/kbn_shared_ux_chrome_navigation.devdocs.json index 41742443ed3bf..76adf1537f4c3 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.devdocs.json +++ b/api_docs/kbn_shared_ux_chrome_navigation.devdocs.json @@ -1,38 +1,22 @@ { "id": "@kbn/shared-ux-chrome-navigation", "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { "classes": [], "functions": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.DefaultNavigation", + "id": "def-public.DefaultNavigation", "type": "Function", "tags": [], "label": "DefaultNavigation", "description": [], "signature": [ - "({ projectNavigationTree, navigationTree, dataTestSubj, panelContentProvider, }: React.PropsWithChildren<", + "React.FunctionComponent<", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.ProjectNavigationDefinition", + "section": "def-public.ProjectNavigationDefinition", "text": "ProjectNavigationDefinition" }, "<", @@ -43,50 +27,47 @@ "section": "def-common.AppDeepLinkId", "text": "AppDeepLinkId" }, - ", string, string> & Props>) => JSX.Element" + ", string, string> & Props>" ], "path": "packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx", "deprecated": false, "trackAdoption": false, + "returnComment": [], "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.DefaultNavigation.$1", + "id": "def-public.DefaultNavigation.$1", "type": "CompoundType", "tags": [], - "label": "{\n projectNavigationTree,\n navigationTree,\n dataTestSubj,\n panelContentProvider,\n}", + "label": "props", "description": [], "signature": [ - "React.PropsWithChildren<", - { - "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", - "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.ProjectNavigationDefinition", - "text": "ProjectNavigationDefinition" - }, - "<", - { - "pluginId": "@kbn/core-chrome-browser", - "scope": "common", - "docId": "kibKbnCoreChromeBrowserPluginApi", - "section": "def-common.AppDeepLinkId", - "text": "AppDeepLinkId" - }, - ", string, string> & Props>" + "P & { children?: React.ReactNode; }" ], - "path": "packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx", + "path": "node_modules/@types/react/index.d.ts", "deprecated": false, - "trackAdoption": false, - "isRequired": true + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/shared-ux-chrome-navigation", + "id": "def-public.DefaultNavigation.$2", + "type": "Any", + "tags": [], + "label": "context", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false } ], - "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -107,7 +88,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "string", "tags": [], "label": "preset", @@ -126,7 +107,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -147,7 +128,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "string", "tags": [], "label": "preset", @@ -166,7 +147,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -187,7 +168,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "string", "tags": [], "label": "preset", @@ -206,7 +187,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -227,7 +208,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "string", "tags": [], "label": "preset", @@ -246,7 +227,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -292,7 +273,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "string", "tags": [], "label": "preset", @@ -311,7 +292,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets", + "id": "def-public.getPresets", "type": "Function", "tags": [], "label": "getPresets", @@ -320,9 +301,9 @@ "(preset: \"all\" | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationGroupPreset", + "section": "def-public.NavigationGroupPreset", "text": "NavigationGroupPreset" }, ") => ", @@ -397,7 +378,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.getPresets.$1", + "id": "def-public.getPresets.$1", "type": "CompoundType", "tags": [], "label": "preset", @@ -406,9 +387,9 @@ "\"all\" | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationGroupPreset", + "section": "def-public.NavigationGroupPreset", "text": "NavigationGroupPreset" } ], @@ -423,7 +404,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.Navigation", + "id": "def-public.Navigation", "type": "Function", "tags": [], "label": "Navigation", @@ -437,7 +418,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.Navigation.$1", + "id": "def-public.Navigation.$1", "type": "Object", "tags": [], "label": "{\n children,\n panelContentProvider,\n unstyled = false,\n dataTestSubj,\n}", @@ -456,7 +437,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationKibanaProvider", + "id": "def-public.NavigationKibanaProvider", "type": "Function", "tags": [], "label": "NavigationKibanaProvider", @@ -474,7 +455,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationKibanaProvider.$1", + "id": "def-public.NavigationKibanaProvider.$1", "type": "CompoundType", "tags": [], "label": "{\n children,\n ...dependencies\n}", @@ -495,7 +476,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationProvider", + "id": "def-public.NavigationProvider", "type": "Function", "tags": [], "label": "NavigationProvider", @@ -506,9 +487,9 @@ "({ children, ...services }: React.PropsWithChildren<", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationServices", + "section": "def-public.NavigationServices", "text": "NavigationServices" }, ">) => JSX.Element" @@ -519,7 +500,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationProvider.$1", + "id": "def-public.NavigationProvider.$1", "type": "CompoundType", "tags": [], "label": "{ children, ...services }", @@ -528,9 +509,9 @@ "React.PropsWithChildren<", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationServices", + "section": "def-public.NavigationServices", "text": "NavigationServices" }, ">" @@ -548,7 +529,7 @@ "interfaces": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.GroupDefinition", + "id": "def-public.GroupDefinition", "type": "Interface", "tags": [], "label": "GroupDefinition", @@ -556,9 +537,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.GroupDefinition", + "section": "def-public.GroupDefinition", "text": "GroupDefinition" }, " extends Omit<", @@ -577,7 +558,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.GroupDefinition.type", + "id": "def-public.GroupDefinition.type", "type": "string", "tags": [], "label": "type", @@ -591,7 +572,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.GroupDefinition.children", + "id": "def-public.GroupDefinition.children", "type": "Array", "tags": [], "label": "children", @@ -615,7 +596,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ItemDefinition", + "id": "def-public.ItemDefinition", "type": "Interface", "tags": [], "label": "ItemDefinition", @@ -623,9 +604,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.ItemDefinition", + "section": "def-public.ItemDefinition", "text": "ItemDefinition" }, " extends Omit<", @@ -644,7 +625,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ItemDefinition.type", + "id": "def-public.ItemDefinition.type", "type": "string", "tags": [], "label": "type", @@ -661,7 +642,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices", + "id": "def-public.NavigationServices", "type": "Interface", "tags": [], "label": "NavigationServices", @@ -674,7 +655,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.basePath", + "id": "def-public.NavigationServices.basePath", "type": "Object", "tags": [], "label": "basePath", @@ -688,7 +669,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.recentlyAccessed$", + "id": "def-public.NavigationServices.recentlyAccessed$", "type": "Object", "tags": [], "label": "recentlyAccessed$", @@ -705,14 +686,14 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.navLinks$", + "id": "def-public.NavigationServices.deepLinks$", "type": "Object", "tags": [], - "label": "navLinks$", + "label": "deepLinks$", "description": [], "signature": [ "Observable", - "" + ">>>" ], "path": "packages/shared-ux/chrome/navigation/types/index.ts", "deprecated": false, @@ -728,7 +709,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.navIsOpen", + "id": "def-public.NavigationServices.navIsOpen", "type": "boolean", "tags": [], "label": "navIsOpen", @@ -739,7 +720,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.navigateToUrl", + "id": "def-public.NavigationServices.navigateToUrl", "type": "Function", "tags": [], "label": "navigateToUrl", @@ -762,7 +743,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.navigateToUrl.$1", + "id": "def-public.NavigationServices.navigateToUrl.$1", "type": "string", "tags": [], "label": "url", @@ -773,7 +754,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.navigateToUrl.$2", + "id": "def-public.NavigationServices.navigateToUrl.$2", "type": "Object", "tags": [], "label": "options", @@ -796,7 +777,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.onProjectNavigationChange", + "id": "def-public.NavigationServices.onProjectNavigationChange", "type": "Function", "tags": [], "label": "onProjectNavigationChange", @@ -818,7 +799,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.onProjectNavigationChange.$1", + "id": "def-public.NavigationServices.onProjectNavigationChange.$1", "type": "Object", "tags": [], "label": "chromeProjectNavigation", @@ -842,7 +823,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.activeNodes$", + "id": "def-public.NavigationServices.activeNodes$", "type": "Object", "tags": [], "label": "activeNodes$", @@ -865,7 +846,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.cloudLinks", + "id": "def-public.NavigationServices.cloudLinks", "type": "Object", "tags": [], "label": "cloudLinks", @@ -887,7 +868,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationServices.isSideNavCollapsed", + "id": "def-public.NavigationServices.isSideNavCollapsed", "type": "boolean", "tags": [], "label": "isSideNavCollapsed", @@ -901,7 +882,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationTreeDefinition", + "id": "def-public.NavigationTreeDefinition", "type": "Interface", "tags": [], "label": "NavigationTreeDefinition", @@ -909,9 +890,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationTreeDefinition", + "section": "def-public.NavigationTreeDefinition", "text": "NavigationTreeDefinition" }, "" @@ -922,7 +903,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationTreeDefinition.body", + "id": "def-public.NavigationTreeDefinition.body", "type": "Array", "tags": [], "label": "body", @@ -932,9 +913,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.RootNavigationItemDefinition", + "section": "def-public.RootNavigationItemDefinition", "text": "RootNavigationItemDefinition" }, "[] | undefined" @@ -945,7 +926,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationTreeDefinition.footer", + "id": "def-public.NavigationTreeDefinition.footer", "type": "Array", "tags": [], "label": "footer", @@ -955,9 +936,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.RootNavigationItemDefinition", + "section": "def-public.RootNavigationItemDefinition", "text": "RootNavigationItemDefinition" }, "[] | undefined" @@ -971,7 +952,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelComponentProps", + "id": "def-public.PanelComponentProps", "type": "Interface", "tags": [], "label": "PanelComponentProps", @@ -982,7 +963,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelComponentProps.closePanel", + "id": "def-public.PanelComponentProps.closePanel", "type": "Function", "tags": [], "label": "closePanel", @@ -1000,7 +981,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelComponentProps.selectedNode", + "id": "def-public.PanelComponentProps.selectedNode", "type": "CompoundType", "tags": [], "label": "selectedNode", @@ -1024,7 +1005,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelComponentProps.activeNodes", + "id": "def-public.PanelComponentProps.activeNodes", "type": "Array", "tags": [], "label": "activeNodes", @@ -1050,7 +1031,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelContent", + "id": "def-public.PanelContent", "type": "Interface", "tags": [], "label": "PanelContent", @@ -1061,7 +1042,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelContent.title", + "id": "def-public.PanelContent.title", "type": "CompoundType", "tags": [], "label": "title", @@ -1075,7 +1056,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PanelContent.content", + "id": "def-public.PanelContent.content", "type": "CompoundType", "tags": [], "label": "content", @@ -1084,9 +1065,9 @@ "React.ComponentType<", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.PanelComponentProps", + "section": "def-public.PanelComponentProps", "text": "PanelComponentProps" }, "> | undefined" @@ -1100,7 +1081,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PresetDefinition", + "id": "def-public.PresetDefinition", "type": "Interface", "tags": [], "label": "PresetDefinition", @@ -1108,17 +1089,17 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.PresetDefinition", + "section": "def-public.PresetDefinition", "text": "PresetDefinition" }, " extends Omit<", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.GroupDefinition", + "section": "def-public.GroupDefinition", "text": "GroupDefinition" }, ", \"type\" | \"children\">" @@ -1129,7 +1110,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PresetDefinition.type", + "id": "def-public.PresetDefinition.type", "type": "string", "tags": [], "label": "type", @@ -1143,7 +1124,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.PresetDefinition.preset", + "id": "def-public.PresetDefinition.preset", "type": "CompoundType", "tags": [], "label": "preset", @@ -1160,7 +1141,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ProjectNavigationDefinition", + "id": "def-public.ProjectNavigationDefinition", "type": "Interface", "tags": [], "label": "ProjectNavigationDefinition", @@ -1168,9 +1149,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.ProjectNavigationDefinition", + "section": "def-public.ProjectNavigationDefinition", "text": "ProjectNavigationDefinition" }, "" @@ -1181,7 +1162,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ProjectNavigationDefinition.projectNavigationTree", + "id": "def-public.ProjectNavigationDefinition.projectNavigationTree", "type": "Array", "tags": [], "label": "projectNavigationTree", @@ -1198,7 +1179,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ProjectNavigationDefinition.navigationTree", + "id": "def-public.ProjectNavigationDefinition.navigationTree", "type": "Object", "tags": [], "label": "navigationTree", @@ -1208,9 +1189,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.NavigationTreeDefinition", + "section": "def-public.NavigationTreeDefinition", "text": "NavigationTreeDefinition" }, " | undefined" @@ -1224,7 +1205,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.RecentlyAccessedDefinition", + "id": "def-public.RecentlyAccessedDefinition", "type": "Interface", "tags": [], "label": "RecentlyAccessedDefinition", @@ -1232,9 +1213,9 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.RecentlyAccessedDefinition", + "section": "def-public.RecentlyAccessedDefinition", "text": "RecentlyAccessedDefinition" }, " extends ", @@ -1246,7 +1227,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.RecentlyAccessedDefinition.type", + "id": "def-public.RecentlyAccessedDefinition.type", "type": "string", "tags": [], "label": "type", @@ -1266,7 +1247,7 @@ "misc": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ContentProvider", + "id": "def-public.ContentProvider", "type": "Type", "tags": [], "label": "ContentProvider", @@ -1275,9 +1256,9 @@ "(nodeId: string) => void | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.PanelContent", + "section": "def-public.PanelContent", "text": "PanelContent" } ], @@ -1288,7 +1269,7 @@ "children": [ { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.ContentProvider.$1", + "id": "def-public.ContentProvider.$1", "type": "string", "tags": [], "label": "nodeId", @@ -1302,7 +1283,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.NavigationGroupPreset", + "id": "def-public.NavigationGroupPreset", "type": "Type", "tags": [], "label": "NavigationGroupPreset", @@ -1319,7 +1300,7 @@ }, { "parentPluginId": "@kbn/shared-ux-chrome-navigation", - "id": "def-common.RootNavigationItemDefinition", + "id": "def-public.RootNavigationItemDefinition", "type": "Type", "tags": [], "label": "RootNavigationItemDefinition", @@ -1327,33 +1308,33 @@ "signature": [ { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.RecentlyAccessedDefinition", + "section": "def-public.RecentlyAccessedDefinition", "text": "RecentlyAccessedDefinition" }, " | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.GroupDefinition", + "section": "def-public.GroupDefinition", "text": "GroupDefinition" }, " | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.PresetDefinition", + "section": "def-public.PresetDefinition", "text": "PresetDefinition" }, " | ", { "pluginId": "@kbn/shared-ux-chrome-navigation", - "scope": "common", + "scope": "public", "docId": "kibKbnSharedUxChromeNavigationPluginApi", - "section": "def-common.ItemDefinition", + "section": "def-public.ItemDefinition", "text": "ItemDefinition" }, "" @@ -1365,5 +1346,21 @@ } ], "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] } } \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 4e0b94e9bc529..cbefde71b2dcc 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; @@ -21,16 +21,16 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 60 | 0 | 49 | 5 | +| 61 | 0 | 48 | 5 | -## Common +## Client ### Functions - + ### Interfaces - + ### Consts, variables and types - + diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 04fd79c4401f0..cd5de2539a5fc 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 7c325e0c75202..e50e1a184f953 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index ef3b271fc9146..89108f8f6c74d 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-11-22 +date: 2023-11-30 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 5d8a0006f4af1..c4abc6c3030c9 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 24eab5c2a0900..95fa6916cfd5a 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 778cf54e9a1bc..11f220328b471 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 1f35be4dd1c81..67835d52decdb 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 45c63d48191ec..62d287cb51626 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index e761195b79725..0b3e0d8fa01be 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_shared_ux_link_redirect_app.devdocs.json index ae8568c37433d..aa7633649473f 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.devdocs.json +++ b/api_docs/kbn_shared_ux_link_redirect_app.devdocs.json @@ -29,7 +29,9 @@ "\nA service-enabled component that provides Kibana-specific functionality to the `RedirectAppLinks`\npure component.\n" ], "signature": [ - "({ children }: { children?: React.ReactNode; }) => JSX.Element" + "({ children, ...props }: React.PropsWithChildren>) => JSX.Element" ], "path": "packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx", "deprecated": false, @@ -38,12 +40,14 @@ { "parentPluginId": "@kbn/shared-ux-link-redirect-app", "id": "def-common.RedirectAppLinks.$1", - "type": "Object", + "type": "CompoundType", "tags": [], - "label": "{ children }", + "label": "{\n children,\n ...props\n}", "description": [], "signature": [ - "{ children?: React.ReactNode; }" + "React.PropsWithChildren>" ], "path": "packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx", "deprecated": false, @@ -64,7 +68,7 @@ "\nUtility component that will intercept click events on children anchor (``) elements to call\n`navigateToUrl` with the link's href. This will trigger SPA friendly navigation when the link points\nto a valid Kibana app.\n" ], "signature": [ - "({ children, navigateToUrl, currentAppId, }: React.PropsWithChildren<", + "({ children, navigateToUrl, currentAppId, ...containerProps }: React.PropsWithChildren<", "RedirectAppLinksComponentProps", ">) => JSX.Element" ], @@ -77,7 +81,7 @@ "id": "def-common.RedirectAppLinks.$1", "type": "CompoundType", "tags": [], - "label": "{\n children,\n navigateToUrl,\n currentAppId,\n}", + "label": "{\n children,\n navigateToUrl,\n currentAppId,\n ...containerProps\n}", "description": [], "signature": [ "React.PropsWithChildren<", @@ -317,9 +321,11 @@ "Props for the `RedirectAppLinks` component." ], "signature": [ + "(", "RedirectAppLinksKibanaDependencies", " | ", - "RedirectAppLinksServices" + "RedirectAppLinksServices", + ") & React.ClassAttributes & React.HTMLAttributes" ], "path": "packages/shared-ux/link/redirect_app/types/index.d.ts", "deprecated": false, diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 0cd6187f1b321..b178e47be27d1 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-11-22 +date: 2023-11-30 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 cf7426d34cdef..7ca3043a0379a 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-11-22 +date: 2023-11-30 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 0f188765fcd7d..6c1d9078effa1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 4a1a3fba8461e..d5aaed84fa92f 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-11-22 +date: 2023-11-30 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 32948afb720d4..4684ab2f3b6b6 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-11-22 +date: 2023-11-30 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 299fcf0c68fdd..a0694f3aa07f3 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-11-22 +date: 2023-11-30 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 da24aee5079a4..f7b4c45615937 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-11-22 +date: 2023-11-30 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 5777f01bbd1b5..15560c5282540 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-11-22 +date: 2023-11-30 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 b7dfd10d953d4..fbbff539e949d 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-11-22 +date: 2023-11-30 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 b24438e90b43d..e33f7c99e1025 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-11-22 +date: 2023-11-30 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 b931ad0ee4e16..e5b3120e6640e 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-11-22 +date: 2023-11-30 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 4a353a04e6684..8b3ea65ba6d1a 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-11-22 +date: 2023-11-30 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 774a85a85aaa7..cb54b848b3415 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-11-22 +date: 2023-11-30 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 0360ea7706ecd..0584ab1f33ac4 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-11-22 +date: 2023-11-30 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 1d8cf0765abc9..d09c5a723b29b 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-11-22 +date: 2023-11-30 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 6d000cbe7917a..3b00ec9d17e02 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-11-22 +date: 2023-11-30 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 373c192e8524a..beb6a227c7310 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-11-22 +date: 2023-11-30 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 948452efeddfe..64b477d1adb9a 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-11-22 +date: 2023-11-30 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 ca7a35e00938f..0e996dd1c5525 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-11-22 +date: 2023-11-30 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 1272a88987cfa..3fb0bcc60df73 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-11-22 +date: 2023-11-30 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 25df84294f42c..63b0d1d980f75 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-11-22 +date: 2023-11-30 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 c3ef3bd47ae28..95790893c57c4 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-11-22 +date: 2023-11-30 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 f928156b219c9..75a29e7ac932b 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-11-22 +date: 2023-11-30 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 3f70d6cf77a94..c4ad61dd837a7 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-11-22 +date: 2023-11-30 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 fc8e06208883d..ecb5b352674e1 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-11-22 +date: 2023-11-30 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 72cf39f11b774..feb38d8322059 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-11-22 +date: 2023-11-30 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 adbcc14f047e7..169fd1a9acf9e 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-11-22 +date: 2023-11-30 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 a40b38888b985..1dd1f32b2f6bf 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_subscription_tracking.devdocs.json b/api_docs/kbn_subscription_tracking.devdocs.json deleted file mode 100644 index ea9cdb7f07167..0000000000000 --- a/api_docs/kbn_subscription_tracking.devdocs.json +++ /dev/null @@ -1,519 +0,0 @@ -{ - "id": "@kbn/subscription-tracking", - "client": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.registerEvents", - "type": "Function", - "tags": [], - "label": "registerEvents", - "description": [ - "\nRegisters the subscription-specific event types" - ], - "signature": [ - "(analyticsClient: Pick<", - { - "pluginId": "@kbn/analytics-client", - "scope": "common", - "docId": "kibKbnAnalyticsClientPluginApi", - "section": "def-common.IAnalyticsClient", - "text": "IAnalyticsClient" - }, - ", \"registerEventType\">) => void" - ], - "path": "packages/kbn-subscription-tracking/src/services.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.registerEvents.$1", - "type": "Object", - "tags": [], - "label": "analyticsClient", - "description": [], - "signature": [ - "Pick<", - { - "pluginId": "@kbn/analytics-client", - "scope": "common", - "docId": "kibKbnAnalyticsClientPluginApi", - "section": "def-common.IAnalyticsClient", - "text": "IAnalyticsClient" - }, - ", \"registerEventType\">" - ], - "path": "packages/kbn-subscription-tracking/src/services.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButton", - "type": "Function", - "tags": [], - "label": "SubscriptionButton", - "description": [ - "\nWrapper around `EuiButton` that provides subscription events" - ], - "signature": [ - "({\n subscriptionContext,\n children,\n ...restProps\n}: ", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionButtonProps", - "text": "SubscriptionButtonProps" - }, - ") => JSX.Element" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButton.$1", - "type": "CompoundType", - "tags": [], - "label": "{\n subscriptionContext,\n children,\n ...restProps\n}", - "description": [], - "signature": [ - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionButtonProps", - "text": "SubscriptionButtonProps" - } - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButtonEmpty", - "type": "Function", - "tags": [], - "label": "SubscriptionButtonEmpty", - "description": [ - "\nWrapper around `EuiButtonEmpty` that provides subscription events" - ], - "signature": [ - "({\n subscriptionContext,\n children,\n ...restProps\n}: ", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionButtonEmptyProps", - "text": "SubscriptionButtonEmptyProps" - }, - ") => JSX.Element" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButtonEmpty.$1", - "type": "CompoundType", - "tags": [], - "label": "{\n subscriptionContext,\n children,\n ...restProps\n}", - "description": [], - "signature": [ - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionButtonEmptyProps", - "text": "SubscriptionButtonEmptyProps" - } - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionLink", - "type": "Function", - "tags": [], - "label": "SubscriptionLink", - "description": [ - "\nWrapper around `EuiLink` that provides subscription events" - ], - "signature": [ - "({\n subscriptionContext,\n children,\n ...restProps\n}: ", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionLinkProps", - "text": "SubscriptionLinkProps" - }, - ") => JSX.Element" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionLink.$1", - "type": "CompoundType", - "tags": [], - "label": "{\n subscriptionContext,\n children,\n ...restProps\n}", - "description": [], - "signature": [ - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.SubscriptionLinkProps", - "text": "SubscriptionLinkProps" - } - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionTrackingProvider", - "type": "Function", - "tags": [], - "label": "SubscriptionTrackingProvider", - "description": [ - "\nExternal services provider" - ], - "signature": [ - "({ children, ...services }: React.PropsWithChildren<", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.Services", - "text": "Services" - }, - ">) => JSX.Element" - ], - "path": "packages/kbn-subscription-tracking/src/services.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionTrackingProvider.$1", - "type": "CompoundType", - "tags": [], - "label": "{ children, ...services }", - "description": [], - "signature": [ - "React.PropsWithChildren<", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.Services", - "text": "Services" - }, - ">" - ], - "path": "packages/kbn-subscription-tracking/src/services.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services", - "type": "Interface", - "tags": [], - "label": "Services", - "description": [], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services.navigateToApp", - "type": "Function", - "tags": [], - "label": "navigateToApp", - "description": [], - "signature": [ - "(app: string, options: { path: string; }) => void" - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services.navigateToApp.$1", - "type": "string", - "tags": [], - "label": "app", - "description": [], - "signature": [ - "string" - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services.navigateToApp.$2", - "type": "Object", - "tags": [], - "label": "options", - "description": [], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services.navigateToApp.$2.path", - "type": "string", - "tags": [], - "label": "path", - "description": [], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.Services.analyticsClient", - "type": "Object", - "tags": [], - "label": "analyticsClient", - "description": [], - "signature": [ - "{ reportEvent: (eventType: string, eventData: EventTypeData) => void; }" - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionContextData", - "type": "Interface", - "tags": [], - "label": "SubscriptionContextData", - "description": [ - "\nA piece of metadata which consists of an identifier of the advertised feature and\nthe `source` (e.g. location) of the subscription element." - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionContextData.source", - "type": "CompoundType", - "tags": [], - "label": "source", - "description": [ - "\nA human-readable identifier describing the location of the beginning of the\nsubscription flow.\nLocation identifiers are prefixed with a solution identifier, e.g. `security__`\n" - ], - "signature": [ - "`observability__${string}` | `security__${string}`" - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionContextData.feature", - "type": "string", - "tags": [], - "label": "feature", - "description": [ - "\nA human-readable identifier describing the feature that is being promoted.\n" - ], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], - "enums": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.EVENT_NAMES", - "type": "Enum", - "tags": [], - "label": "EVENT_NAMES", - "description": [], - "path": "packages/kbn-subscription-tracking/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], - "misc": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButtonEmptyProps", - "type": "Type", - "tags": [], - "label": "SubscriptionButtonEmptyProps", - "description": [], - "signature": [ - "((", - "DisambiguateSet", - " & ", - "CommonEuiButtonEmptyProps", - " & { onClick?: React.MouseEventHandler | undefined; } & React.ButtonHTMLAttributes) | (", - "DisambiguateSet", - "<", - "EuiButtonEmptyPropsForButton", - ", EuiButtonEmptyPropsForAnchor> & ", - "CommonEuiButtonEmptyProps", - " & { href?: string | undefined; onClick?: React.MouseEventHandler | undefined; } & React.AnchorHTMLAttributes)) & CommonProps" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionButtonProps", - "type": "Type", - "tags": [], - "label": "SubscriptionButtonProps", - "description": [], - "signature": [ - "EuiButtonProps", - " & CommonProps" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionLinkProps", - "type": "Type", - "tags": [], - "label": "SubscriptionLinkProps", - "description": [], - "signature": [ - "((", - "DisambiguateSet", - "<", - "EuiLinkButtonProps", - ", ", - "EuiLinkAnchorProps", - "> & ", - "EuiLinkAnchorProps", - ") | (", - "DisambiguateSet", - "<", - "EuiLinkAnchorProps", - ", ", - "EuiLinkButtonProps", - "> & ", - "EuiLinkButtonProps", - ")) & CommonProps" - ], - "path": "packages/kbn-subscription-tracking/src/subscription_elements.tsx", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], - "objects": [ - { - "parentPluginId": "@kbn/subscription-tracking", - "id": "def-common.SubscriptionTrackingContext", - "type": "Object", - "tags": [], - "label": "SubscriptionTrackingContext", - "description": [], - "signature": [ - "React.Context<", - { - "pluginId": "@kbn/subscription-tracking", - "scope": "common", - "docId": "kibKbnSubscriptionTrackingPluginApi", - "section": "def-common.Services", - "text": "Services" - }, - " | null>" - ], - "path": "packages/kbn-subscription-tracking/src/services.tsx", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ] - } -} \ No newline at end of file diff --git a/api_docs/kbn_subscription_tracking.mdx b/api_docs/kbn_subscription_tracking.mdx deleted file mode 100644 index 3336d527f6639..0000000000000 --- a/api_docs/kbn_subscription_tracking.mdx +++ /dev/null @@ -1,42 +0,0 @@ ---- -#### -#### 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: kibKbnSubscriptionTrackingPluginApi -slug: /kibana-dev-docs/api/kbn-subscription-tracking -title: "@kbn/subscription-tracking" -image: https://source.unsplash.com/400x175/?github -description: API docs for the @kbn/subscription-tracking plugin -date: 2023-11-22 -tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/subscription-tracking'] ---- -import kbnSubscriptionTrackingObj from './kbn_subscription_tracking.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 | -|-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 16 | 0 | - -## Common - -### Objects - - -### Functions - - -### Interfaces - - -### Enums - - -### Consts, variables and types - - diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 688262e55e95b..a8f8395a98c3f 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.devdocs.json b/api_docs/kbn_test.devdocs.json index 67c1c7a4a48c1..68147342a7a5e 100644 --- a/api_docs/kbn_test.devdocs.json +++ b/api_docs/kbn_test.devdocs.json @@ -3136,7 +3136,7 @@ "signature": [ "Pick<", "ServerlessOptions", - ", \"host\" | \"tag\" | \"image\" | \"resources\"> | undefined" + ", \"host\" | \"tag\" | \"image\" | \"resources\" | \"kibanaUrl\"> | undefined" ], "path": "packages/kbn-test/src/es/test_es_cluster.ts", "deprecated": false, diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 12c1d24c0532c..a7682f42eff71 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-11-22 +date: 2023-11-30 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 b42f11f39e38b..965e4159cbf66 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-11-22 +date: 2023-11-30 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 04be847785c0b..15a7ac54189ee 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.devdocs.json b/api_docs/kbn_text_based_editor.devdocs.json index 252a6537ae639..69205611cafdf 100644 --- a/api_docs/kbn_text_based_editor.devdocs.json +++ b/api_docs/kbn_text_based_editor.devdocs.json @@ -192,7 +192,9 @@ "type": "CompoundType", "tags": [], "label": "query", - "description": [], + "description": [ + "The aggregate type query" + ], "signature": [ "{ sql: string; } | { esql: string; }" ], @@ -206,7 +208,9 @@ "type": "Function", "tags": [], "label": "onTextLangQueryChange", - "description": [], + "description": [ + "Callback running everytime the query changes" + ], "signature": [ "(query: ", { @@ -252,14 +256,47 @@ "type": "Function", "tags": [], "label": "onTextLangQuerySubmit", - "description": [], + "description": [ + "Callback running when the user submits the query" + ], "signature": [ - "() => void" + "(query?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined) => void" ], "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "@kbn/text-based-editor", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQuerySubmit.$1", + "type": "CompoundType", + "tags": [], + "label": "query", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], "returnComment": [] }, { @@ -268,7 +305,9 @@ "type": "Function", "tags": [], "label": "expandCodeEditor", - "description": [], + "description": [ + "Can be used to expand/minimize the editor" + ], "signature": [ "(status: boolean) => void" ], @@ -300,7 +339,9 @@ "type": "boolean", "tags": [], "label": "isCodeEditorExpanded", - "description": [], + "description": [ + "If it is true, the editor initializes with height EDITOR_INITIAL_HEIGHT_EXPANDED" + ], "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", "deprecated": false, "trackAdoption": false @@ -311,7 +352,9 @@ "type": "CompoundType", "tags": [], "label": "detectTimestamp", - "description": [], + "description": [ + "If it is true, the editor displays the message @timestamp found\nThe text based queries are relying on adhoc dataviews which\ncan have an @timestamp timefield or nothing" + ], "signature": [ "boolean | undefined" ], @@ -325,7 +368,9 @@ "type": "Array", "tags": [], "label": "errors", - "description": [], + "description": [ + "Array of errors" + ], "signature": [ "Error[] | undefined" ], @@ -339,7 +384,9 @@ "type": "string", "tags": [], "label": "warning", - "description": [], + "description": [ + "Warning string as it comes from ES" + ], "signature": [ "string | undefined" ], @@ -353,7 +400,9 @@ "type": "CompoundType", "tags": [], "label": "isDisabled", - "description": [], + "description": [ + "Disables the editor" + ], "signature": [ "boolean | undefined" ], @@ -367,7 +416,9 @@ "type": "CompoundType", "tags": [], "label": "isDarkMode", - "description": [], + "description": [ + "Indicator if the editor is on dark mode" + ], "signature": [ "boolean | undefined" ], @@ -395,7 +446,9 @@ "type": "CompoundType", "tags": [], "label": "hideMinimizeButton", - "description": [], + "description": [ + "If true it hides the minimize button and the user can't return to the minimized version\nUseful when the application doesn't want to give this capability" + ], "signature": [ "boolean | undefined" ], @@ -409,7 +462,41 @@ "type": "CompoundType", "tags": [], "label": "hideRunQueryText", - "description": [], + "description": [ + "Hide the Run query information which appears on the footer" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/text-based-editor", + "id": "def-public.TextBasedLanguagesEditorProps.editorIsInline", + "type": "CompoundType", + "tags": [], + "label": "editorIsInline", + "description": [ + "This is used for applications (such as the inline editing flyout in dashboards)\nwhich want to add the editor without being part of the Unified search component\nIt renders a submit query button inside the editor" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/text-based-editor", + "id": "def-public.TextBasedLanguagesEditorProps.disableSubmitAction", + "type": "CompoundType", + "tags": [], + "label": "disableSubmitAction", + "description": [ + "Disables the submit query action" + ], "signature": [ "boolean | undefined" ], diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index 8ed115b7dfcae..10666168c60de 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 22 | 0 | 21 | 0 | +| 25 | 0 | 10 | 0 | ## Client diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 8858b27a2e9f9..2c2ac6977b613 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-11-22 +date: 2023-11-30 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 8e226116aedcd..eb56a5e6d8111 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-11-22 +date: 2023-11-30 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 ee161b66fec8b..64f2ca72e0f6c 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_ui_actions_browser.devdocs.json index 7a589d952a59a..aabb32029e0d6 100644 --- a/api_docs/kbn_ui_actions_browser.devdocs.json +++ b/api_docs/kbn_ui_actions_browser.devdocs.json @@ -438,21 +438,6 @@ ], "enums": [], "misc": [ - { - "parentPluginId": "@kbn/ui-actions-browser", - "id": "def-common.CATEGORIZE_FIELD_TRIGGER", - "type": "string", - "tags": [], - "label": "CATEGORIZE_FIELD_TRIGGER", - "description": [], - "signature": [ - "\"CATEGORIZE_FIELD_TRIGGER\"" - ], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/ui-actions-browser", "id": "def-common.PresentableGrouping", @@ -522,53 +507,6 @@ } ], "objects": [ - { - "parentPluginId": "@kbn/ui-actions-browser", - "id": "def-common.categorizeFieldTrigger", - "type": "Object", - "tags": [], - "label": "categorizeFieldTrigger", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/ui-actions-browser", - "id": "def-common.categorizeFieldTrigger.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ui-actions-browser", - "id": "def-common.categorizeFieldTrigger.title", - "type": "string", - "tags": [], - "label": "title", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/ui-actions-browser", - "id": "def-common.categorizeFieldTrigger.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/ui-actions-browser", "id": "def-common.defaultTrigger", diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 433e8ab8a7279..a236fa416ab5b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 49 | 0 | 35 | 0 | +| 44 | 0 | 30 | 0 | ## Common diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index b581a3d23f606..d66a0a07c473a 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-11-22 +date: 2023-11-30 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 b41b4357e893d..535c3a53c1516 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index 680b20ebbd804..8a805b9c34be1 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 2fcfa638bab6c..d8b73a965e507 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index e7173290f9ba4..5c15a0778052f 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index 2eea2144020c7..296276d466cc5 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index 1ff3eed1cc2e4..b792fa0b69c57 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index c27040569e27a..f918fb5539694 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index e0133e1a2a47d..1c8d5ab8767f5 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-11-22 +date: 2023-11-30 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 11608996f3376..052726b6c14f7 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-11-22 +date: 2023-11-30 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 cc63183d0edf8..758b2caa0344c 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-11-22 +date: 2023-11-30 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.devdocs.json b/api_docs/kbn_utils.devdocs.json index c2f4eefd1be61..88821550bc555 100644 --- a/api_docs/kbn_utils.devdocs.json +++ b/api_docs/kbn_utils.devdocs.json @@ -19,6 +19,23 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/utils", + "id": "def-common.buildDataPaths", + "type": "Function", + "tags": [], + "label": "buildDataPaths", + "description": [], + "signature": [ + "() => string[]" + ], + "path": "packages/kbn-utils/src/path/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/utils", "id": "def-common.concatStreamProviders", diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index ac19150f5b3e1..f7eafb03baf16 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 24 | 0 | 14 | 0 | +| 25 | 0 | 15 | 0 | ## Common diff --git a/api_docs/kbn_visualization_ui_components.devdocs.json b/api_docs/kbn_visualization_ui_components.devdocs.json index bd1f417ecd328..7390e6b1b8d42 100644 --- a/api_docs/kbn_visualization_ui_components.devdocs.json +++ b/api_docs/kbn_visualization_ui_components.devdocs.json @@ -716,7 +716,7 @@ "label": "FieldPicker", "description": [], "signature": [ - "({\n selectedOptions,\n options,\n onChoose,\n onDelete,\n fieldIsInvalid,\n ['data-test-subj']: dataTestSub,\n ...rest\n}: ", + "(props: ", "FieldPickerProps", ") => JSX.Element" ], @@ -729,7 +729,7 @@ "id": "def-public.FieldPicker.$1", "type": "Object", "tags": [], - "label": "{\n selectedOptions,\n options,\n onChoose,\n onDelete,\n fieldIsInvalid,\n ['data-test-subj']: dataTestSub,\n ...rest\n}", + "label": "props", "description": [], "signature": [ "FieldPickerProps", diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 8255ac9d74b7c..6ad807f619515 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 5f5969d7ab4e7..a992da3c359a9 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index bce93b3c1292a..9a5d9821e3009 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 5552282db5992..7ccd7e26a3a24 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 48a787c1a3171..471d42d2193cf 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index 4cc2a07f5bad4..06032a3c41846 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -1021,111 +1021,111 @@ }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" + "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" + "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" + "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" + "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" + "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" + "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" + "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" + "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" + "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" + "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" + "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" + "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" + "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" + "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx" + "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/users/users_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/roles/roles_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/login/login_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" }, { "plugin": "security", - "path": "x-pack/plugins/security/public/account_management/account_management_app.tsx" + "path": "x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx" }, { "plugin": "security", diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 5cfcf0433b07b..67601786322e7 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-11-22 +date: 2023-11-30 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 a72f176d44dca..b19e8f9edb68e 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-11-22 +date: 2023-11-30 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 b180f846db1bc..e8caae0e55d71 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 60b7cbe97d8d6..96eb1b3d2cc47 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -360,7 +360,7 @@ "\nGets the Lens embeddable's datasource and visualization states\nupdates the embeddable input" ], "signature": [ - "(datasourceState: unknown, visualizationState: unknown) => Promise" + "(datasourceState: unknown, visualizationState: unknown, visualizationType?: string | undefined) => Promise" ], "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", "deprecated": false, @@ -395,6 +395,67 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "lens", + "id": "def-public.Embeddable.updateVisualization.$3", + "type": "string", + "tags": [], + "label": "visualizationType", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "lens", + "id": "def-public.Embeddable.updateSuggestion", + "type": "Function", + "tags": [], + "label": "updateSuggestion", + "description": [], + "signature": [ + "(attrs: ", + { + "pluginId": "lens", + "scope": "public", + "docId": "kibLensPluginApi", + "section": "def-public.LensSavedObjectAttributes", + "text": "LensSavedObjectAttributes" + }, + ") => Promise" + ], + "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.Embeddable.updateSuggestion.$1", + "type": "Object", + "tags": [], + "label": "attrs", + "description": [], + "signature": [ + { + "pluginId": "lens", + "scope": "public", + "docId": "kibLensPluginApi", + "section": "def-public.LensSavedObjectAttributes", + "text": "LensSavedObjectAttributes" + } + ], + "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -1401,7 +1462,7 @@ "label": "seriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\"" + "\"area\" | \"line\" | \"bar\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, @@ -5131,6 +5192,20 @@ "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.SuggestionRequest.datasourceId", + "type": "string", + "tags": [], + "label": "datasourceId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -5222,6 +5297,20 @@ "path": "x-pack/plugins/lens/public/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TableSuggestion.notAssignedMetrics", + "type": "CompoundType", + "tags": [], + "label": "notAssignedMetrics", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/lens/public/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -8708,7 +8797,7 @@ "label": "seriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, @@ -9035,7 +9124,7 @@ "label": "preferredSeriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, @@ -10469,7 +10558,7 @@ "label": "SeriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "x-pack/plugins/lens/public/visualizations/xy/types.ts", "deprecated": false, diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 79e0864267522..862a987c1f8c2 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 625 | 0 | 526 | 60 | +| 630 | 0 | 531 | 60 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 9a6292db36d89..b1085b980b72a 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-11-22 +date: 2023-11-30 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 f7fcef15a121d..0a9a4871f7cce 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-11-22 +date: 2023-11-30 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 2fa21b35baeb7..1004dd938427a 100644 --- a/api_docs/licensing.devdocs.json +++ b/api_docs/licensing.devdocs.json @@ -822,6 +822,10 @@ "plugin": "ml", "path": "x-pack/plugins/ml/public/plugin.ts" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/public/plugin.ts" + }, { "plugin": "profiling", "path": "x-pack/plugins/profiling/public/components/contexts/license/license_context.tsx" diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 7b1400685f2db..2fe5a5ea7a53e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index c640cea104845..2bb1fb4d0b977 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index e564a0c3a3db0..9a5acc3c44e66 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/log_explorer.mdx b/api_docs/log_explorer.mdx index 54babc28f1b5d..6bac40c5a9d82 100644 --- a/api_docs/log_explorer.mdx +++ b/api_docs/log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logExplorer title: "logExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logExplorer plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logExplorer'] --- import logExplorerObj from './log_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 6290d6dc098ed..9a353f9acd838 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 060f9043b9632..ec4dcd167f44e 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-11-22 +date: 2023-11-30 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 cb579027ba993..9f99a794ac04a 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-11-22 +date: 2023-11-30 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 522e32357ea8d..0d76e7b470091 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.devdocs.json b/api_docs/metrics_data_access.devdocs.json index fa981b59de281..d4c9bae22502c 100644 --- a/api_docs/metrics_data_access.devdocs.json +++ b/api_docs/metrics_data_access.devdocs.json @@ -822,7 +822,7 @@ "label": "InventoryVisType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\"" + "\"area\" | \"line\" | \"bar\"" ], "path": "x-pack/plugins/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 4fc0a7d76c9f1..2f2461b9b68c9 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index bb4182343afc3..87b4da2473a9e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.devdocs.json b/api_docs/mock_idp_plugin.devdocs.json new file mode 100644 index 0000000000000..9c7b5bd1558d8 --- /dev/null +++ b/api_docs/mock_idp_plugin.devdocs.json @@ -0,0 +1,410 @@ +{ + "id": "mockIdpPlugin", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createMockIdpMetadata", + "type": "Function", + "tags": [], + "label": "createMockIdpMetadata", + "description": [ + "\nCreates XML metadata for our mock identity provider.\n\nThis can be saved to file and used to configure Elasticsearch SAML realm.\n" + ], + "signature": [ + "(kibanaUrl: string) => Promise" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createMockIdpMetadata.$1", + "type": "string", + "tags": [], + "label": "kibanaUrl", + "description": [ + "Fully qualified URL where Kibana is hosted (including base path)" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse", + "type": "Function", + "tags": [], + "label": "createSAMLResponse", + "description": [ + "\nCreates a SAML response that can be passed directly to the Kibana ACS endpoint to authenticate a user.\n" + ], + "signature": [ + "(options: { kibanaUrl: string; authnRequestId?: string | undefined; username: string; fullname?: string | undefined; email?: string | undefined; roles: string[]; }) => Promise" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.kibanaUrl", + "type": "string", + "tags": [], + "label": "kibanaUrl", + "description": [ + "Fully qualified URL where Kibana is hosted (including base path)" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.authnRequestId", + "type": "string", + "tags": [], + "label": "authnRequestId", + "description": [ + "ID from SAML authentication request" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.fullname", + "type": "string", + "tags": [], + "label": "fullname", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.email", + "type": "string", + "tags": [], + "label": "email", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.createSAMLResponse.$1.roles", + "type": "Array", + "tags": [], + "label": "roles", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.ensureSAMLRoleMapping", + "type": "Function", + "tags": [], + "label": "ensureSAMLRoleMapping", + "description": [ + "\nCreates the role mapping required for developers to authenticate using SAML." + ], + "signature": [ + "(client: ", + "default", + ") => Promise" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.ensureSAMLRoleMapping.$1", + "type": "Object", + "tags": [], + "label": "client", + "description": [], + "signature": [ + "default" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.parseSAMLAuthnRequest", + "type": "Function", + "tags": [], + "label": "parseSAMLAuthnRequest", + "description": [], + "signature": [ + "(samlRequest: string) => Promise<{ AssertionConsumerServiceURL: string; Destination: string; ID: string; IssueInstant: string; }>" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.parseSAMLAuthnRequest.$1", + "type": "string", + "tags": [], + "label": "samlRequest", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-mock-idp-plugin/common/utils.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ATTRIBUTE_EMAIL", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ATTRIBUTE_EMAIL", + "description": [], + "signature": [ + "\"http://saml.elastic-cloud.com/attributes/email\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ATTRIBUTE_NAME", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ATTRIBUTE_NAME", + "description": [], + "signature": [ + "\"http://saml.elastic-cloud.com/attributes/name\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ATTRIBUTE_PRINCIPAL", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ATTRIBUTE_PRINCIPAL", + "description": [], + "signature": [ + "\"http://saml.elastic-cloud.com/attributes/principal\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ATTRIBUTE_ROLES", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ATTRIBUTE_ROLES", + "description": [], + "signature": [ + "\"http://saml.elastic-cloud.com/attributes/roles\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ENTITY_ID", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ENTITY_ID", + "description": [], + "signature": [ + "\"urn:mock-idp\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_LOGIN_PATH", + "type": "string", + "tags": [], + "label": "MOCK_IDP_LOGIN_PATH", + "description": [], + "signature": [ + "\"/mock_idp/login\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_LOGOUT_PATH", + "type": "string", + "tags": [], + "label": "MOCK_IDP_LOGOUT_PATH", + "description": [], + "signature": [ + "\"/mock_idp/logout\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_METADATA_PATH", + "type": "string", + "tags": [], + "label": "MOCK_IDP_METADATA_PATH", + "description": [], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_PLUGIN_PATH", + "type": "string", + "tags": [], + "label": "MOCK_IDP_PLUGIN_PATH", + "description": [], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_REALM_NAME", + "type": "string", + "tags": [], + "label": "MOCK_IDP_REALM_NAME", + "description": [], + "signature": [ + "\"mock-idp\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "mockIdpPlugin", + "id": "def-common.MOCK_IDP_ROLE_MAPPING_NAME", + "type": "string", + "tags": [], + "label": "MOCK_IDP_ROLE_MAPPING_NAME", + "description": [], + "signature": [ + "\"mock-idp-mapping\"" + ], + "path": "packages/kbn-mock-idp-plugin/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx new file mode 100644 index 0000000000000..cb94d7968da0f --- /dev/null +++ b/api_docs/mock_idp_plugin.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: kibMockIdpPluginPluginApi +slug: /kibana-dev-docs/api/mockIdpPlugin +title: "mockIdpPlugin" +image: https://source.unsplash.com/400x175/?github +description: API docs for the mockIdpPlugin plugin +date: 2023-11-30 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] +--- +import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; + + + +Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 25 | 0 | 19 | 0 | + +## Common + +### Functions + + +### Consts, variables and types + + diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 21439b9082905..82b128340cc10 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-11-22 +date: 2023-11-30 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 276507f3f0b12..3ebf47a7cfc0b 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-11-22 +date: 2023-11-30 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 098d1ab3e9218..ab40985b66f6a 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-11-22 +date: 2023-11-30 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 df0e54cc665e1..38f4755e1d6b1 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index dd556a8ddb4bd..f26b7ebcfe26f 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 77d976844d91e..06497348df1f9 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-11-22 +date: 2023-11-30 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 c05a0a49927fd..93c7fda5a9375 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -784,7 +784,7 @@ }, " | undefined; list: () => string[]; }; selectedAlertId?: string | undefined; } & ", "CommonProps", - " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | keyof React.HTMLAttributes | \"css\"> & { ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject | null | undefined; }, \"type\" | \"prefix\" | \"key\" | \"id\" | \"defaultValue\" | \"security\" | \"children\" | \"ref\" | \"onChange\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"className\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"hidden\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"title\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"color\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDown\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClick\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"data-test-subj\" | \"css\" | \"paddingSize\" | \"size\" | \"onClose\" | \"as\" | \"maxWidth\" | \"ownFocus\" | \"hideCloseButton\" | \"closeButtonProps\" | \"closeButtonPosition\" | \"maskProps\" | \"outsideClickCloses\" | \"side\" | \"pushMinBreakpoint\" | \"pushAnimation\" | \"focusTrapProps\" | \"includeFixedHeadersInFocusTrap\">, \"type\" | \"prefix\" | \"key\" | \"id\" | \"defaultValue\" | \"security\" | \"alert\" | \"children\" | \"onChange\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"className\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"hidden\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"title\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"color\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDown\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClick\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"data-test-subj\" | \"css\" | \"alerts\" | \"paddingSize\" | \"size\" | \"onClose\" | \"as\" | \"maxWidth\" | \"ownFocus\" | \"hideCloseButton\" | \"closeButtonProps\" | \"closeButtonPosition\" | \"maskProps\" | \"outsideClickCloses\" | \"side\" | \"pushMinBreakpoint\" | \"pushAnimation\" | \"focusTrapProps\" | \"includeFixedHeadersInFocusTrap\" | \"isInApp\" | \"observabilityRuleTypeRegistry\" | \"selectedAlertId\"> & { ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject | null | undefined; }> & { readonly _result: ({ alert, alerts, isInApp, observabilityRuleTypeRegistry, onClose, selectedAlertId, }: AlertsFlyoutProps) => JSX.Element | null; }" + " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | keyof React.HTMLAttributes | \"css\"> & { ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject | null | undefined; }, \"type\" | \"prefix\" | \"key\" | \"id\" | \"defaultValue\" | \"security\" | \"children\" | \"ref\" | \"onChange\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"className\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"hidden\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"title\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"color\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDown\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClick\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"data-test-subj\" | \"css\" | \"paddingSize\" | \"size\" | \"onClose\" | \"maxWidth\" | \"ownFocus\" | \"hideCloseButton\" | \"closeButtonProps\" | \"closeButtonPosition\" | \"maskProps\" | \"outsideClickCloses\" | \"side\" | \"pushMinBreakpoint\" | \"pushAnimation\" | \"focusTrapProps\" | \"includeFixedHeadersInFocusTrap\" | \"as\">, \"type\" | \"prefix\" | \"key\" | \"id\" | \"defaultValue\" | \"security\" | \"alert\" | \"children\" | \"onChange\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"className\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"hidden\" | \"lang\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"style\" | \"tabIndex\" | \"title\" | \"translate\" | \"radioGroup\" | \"role\" | \"about\" | \"datatype\" | \"inlist\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"color\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-label\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onError\" | \"onErrorCapture\" | \"onKeyDown\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClick\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"data-test-subj\" | \"css\" | \"alerts\" | \"paddingSize\" | \"size\" | \"onClose\" | \"maxWidth\" | \"ownFocus\" | \"hideCloseButton\" | \"closeButtonProps\" | \"closeButtonPosition\" | \"maskProps\" | \"outsideClickCloses\" | \"side\" | \"pushMinBreakpoint\" | \"pushAnimation\" | \"focusTrapProps\" | \"includeFixedHeadersInFocusTrap\" | \"as\" | \"isInApp\" | \"observabilityRuleTypeRegistry\" | \"selectedAlertId\"> & { ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject | null | undefined; }> & { readonly _result: ({ alert, alerts, isInApp, observabilityRuleTypeRegistry, onClose, selectedAlertId, }: AlertsFlyoutProps) => JSX.Element | null; }" ], "path": "x-pack/plugins/observability/public/index.ts", "deprecated": false, @@ -2636,6 +2636,26 @@ "path": "x-pack/plugins/observability/public/plugin.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsSetup.licensing", + "type": "Object", + "tags": [], + "label": "licensing", + "description": [], + "signature": [ + { + "pluginId": "licensing", + "scope": "public", + "docId": "kibLicensingPluginApi", + "section": "def-public.LicensingPluginSetup", + "text": "LicensingPluginSetup" + } + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -3339,7 +3359,15 @@ "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", @@ -3358,7 +3386,15 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", @@ -3566,7 +3602,15 @@ "label": "fields", "description": [], "signature": [ - "OutputOf> & OutputOf> & TAdditionalMetaFields" + "OutputOf> & OutputOf> & TAdditionalMetaFields" ], "path": "x-pack/plugins/observability/public/typings/alerts.ts", "deprecated": false, @@ -4366,7 +4410,15 @@ "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", @@ -4385,7 +4437,15 @@ "label": "options", "description": [], "signature": [ - "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", + "{ fields: OutputOf> & Record; formatters: { asDuration: (value: ", "Maybe", ", { defaultValue, extended }?: FormatterOptions) => string; asPercent: (numerator: ", "Maybe", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 44a7110a2a16a..0e541476cce4a 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 580 | 2 | 571 | 17 | +| 581 | 2 | 572 | 17 | ## Client diff --git a/api_docs/observability_a_i_assistant.devdocs.json b/api_docs/observability_a_i_assistant.devdocs.json index 29b5cec0de507..f72f26ee642a6 100644 --- a/api_docs/observability_a_i_assistant.devdocs.json +++ b/api_docs/observability_a_i_assistant.devdocs.json @@ -19,7 +19,7 @@ "section": "def-common.Message", "text": "Message" }, - "[]; title: string; } & ", + "[]; title: string; dataTestSubj?: string | undefined; } & ", { "pluginId": "@kbn/shared-ux-utility", "scope": "common", @@ -415,7 +415,15 @@ "Type", "; }>]>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -814,7 +822,15 @@ "Type", "; }>]>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -1319,7 +1335,15 @@ "Type", "; }>]>; }> | undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", + " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/functions/kb_status\": { endpoint: \"GET /internal/observability_ai_assistant/functions/kb_status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 80ffd28c0a926..2f659f30287de 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_log_explorer.mdx b/api_docs/observability_log_explorer.mdx index b49f7840c2bd2..00ae04b0f505a 100644 --- a/api_docs/observability_log_explorer.mdx +++ b/api_docs/observability_log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogExplorer title: "observabilityLogExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogExplorer plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogExplorer'] --- import observabilityLogExplorerObj from './observability_log_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index e017a237f097c..05ca4c0f3f887 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.devdocs.json b/api_docs/observability_shared.devdocs.json index 61d166ec80f8d..66888b1e6a822 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -315,6 +315,23 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.allCasesPermissions", + "type": "Function", + "tags": [], + "label": "allCasesPermissions", + "description": [], + "signature": [ + "() => { all: boolean; create: boolean; read: boolean; update: boolean; delete: boolean; push: boolean; connectors: boolean; settings: boolean; }" + ], + "path": "x-pack/plugins/observability_shared/public/utils/cases_permissions.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.createEsParams", @@ -414,6 +431,53 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBar", + "type": "Function", + "tags": [], + "label": "EmbeddableProfilingSearchBar", + "description": [], + "signature": [ + "(props: ", + { + "pluginId": "observabilityShared", + "scope": "public", + "docId": "kibObservabilitySharedPluginApi", + "section": "def-public.EmbeddableProfilingSearchBarProps", + "text": "EmbeddableProfilingSearchBarProps" + }, + ") => JSX.Element" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBar.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + { + "pluginId": "observabilityShared", + "scope": "public", + "docId": "kibObservabilitySharedPluginApi", + "section": "def-public.EmbeddableProfilingSearchBarProps", + "text": "EmbeddableProfilingSearchBarProps" + } + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.getContextMenuItemsFromActions", @@ -739,7 +803,7 @@ "label": "noCasesPermissions", "description": [], "signature": [ - "() => { all: boolean; create: boolean; read: boolean; update: boolean; delete: boolean; push: boolean; connectors: boolean; }" + "() => { all: boolean; create: boolean; read: boolean; update: boolean; delete: boolean; push: boolean; connectors: boolean; settings: boolean; }" ], "path": "x-pack/plugins/observability_shared/public/utils/cases_permissions.ts", "deprecated": false, @@ -826,7 +890,7 @@ "DisambiguateSet", ", Omit, \"href\">> & Omit, \"href\">) | (", "DisambiguateSet", - ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"xs\" | \"l\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", + ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"l\" | \"xs\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", "IconType", " | undefined; iconProps?: Omit<", "EuiIconProps", @@ -865,7 +929,7 @@ "DisambiguateSet", ", Omit, \"href\">> & Omit, \"href\">) | (", "DisambiguateSet", - ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"xs\" | \"l\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", + ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"l\" | \"xs\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", "IconType", " | undefined; iconProps?: Omit<", "EuiIconProps", @@ -896,7 +960,7 @@ "CommonProps", " & Omit, \"color\"> & { bordered?: boolean | undefined; flush?: boolean | undefined; gutterSize?: \"m\" | \"none\" | \"s\" | undefined; listItems?: ", "EuiListGroupItemProps", - "[] | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; size?: \"m\" | \"s\" | \"xs\" | \"l\" | undefined; maxWidth?: boolean | ", + "[] | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; size?: \"m\" | \"s\" | \"l\" | \"xs\" | undefined; maxWidth?: boolean | ", "Property", ".MaxWidth | undefined; showToolTips?: boolean | undefined; wrapText?: boolean | undefined; ariaLabelledby?: string | undefined; }) => JSX.Element" ], @@ -916,7 +980,7 @@ "CommonProps", " & Omit, \"color\"> & { bordered?: boolean | undefined; flush?: boolean | undefined; gutterSize?: \"m\" | \"none\" | \"s\" | undefined; listItems?: ", "EuiListGroupItemProps", - "[] | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; size?: \"m\" | \"s\" | \"xs\" | \"l\" | undefined; maxWidth?: boolean | ", + "[] | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; size?: \"m\" | \"s\" | \"l\" | \"xs\" | undefined; maxWidth?: boolean | ", "Property", ".MaxWidth | undefined; showToolTips?: boolean | undefined; wrapText?: boolean | undefined; ariaLabelledby?: string | undefined; }" ], @@ -1468,30 +1532,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "observabilityShared", - "id": "def-public.useGetUserCasesPermissions", - "type": "Function", - "tags": [], - "label": "useGetUserCasesPermissions", - "description": [], - "signature": [ - "() => ", - { - "pluginId": "cases", - "scope": "common", - "docId": "kibCasesPluginApi", - "section": "def-common.CasesPermissions", - "text": "CasesPermissions" - } - ], - "path": "x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "observabilityShared", "id": "def-public.useInspectorContext", @@ -1912,6 +1952,138 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps", + "type": "Interface", + "tags": [], + "label": "EmbeddableProfilingSearchBarProps", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.kuery", + "type": "string", + "tags": [], + "label": "kuery", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.showDatePicker", + "type": "CompoundType", + "tags": [], + "label": "showDatePicker", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.onQuerySubmit", + "type": "Function", + "tags": [], + "label": "onQuerySubmit", + "description": [], + "signature": [ + "(params: { dateRange: { from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }; query: string; }) => void" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.onQuerySubmit.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.onQuerySubmit.$1.dateRange", + "type": "Object", + "tags": [], + "label": "dateRange", + "description": [], + "signature": [ + "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.onQuerySubmit.$1.query", + "type": "string", + "tags": [], + "label": "query", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.onRefresh", + "type": "Function", + "tags": [], + "label": "onRefresh", + "description": [], + "signature": [ + "() => void" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.rangeFrom", + "type": "string", + "tags": [], + "label": "rangeFrom", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EmbeddableProfilingSearchBarProps.rangeTo", + "type": "string", + "tags": [], + "label": "rangeTo", + "description": [], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.FetcherResult", @@ -2620,6 +2792,23 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observabilityShared", + "id": "def-public.EMBEDDABLE_PROFILING_SEARCH_BAR", + "type": "string", + "tags": [], + "label": "EMBEDDABLE_PROFILING_SEARCH_BAR", + "description": [ + "Profiling search bar embeddable key" + ], + "signature": [ + "\"EMBEDDABLE_PROFILING_SEARCH_BAR\"" + ], + "path": "x-pack/plugins/observability_shared/public/components/profiling/embeddables/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observabilityShared", "id": "def-public.LazyObservabilityPageTemplateProps", @@ -2895,7 +3084,7 @@ "DisambiguateSet", ", Omit, \"href\">> & Omit, \"href\">) | (", "DisambiguateSet", - ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"xs\" | \"l\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", + ", \"href\">, React.ButtonHTMLAttributes> & React.ButtonHTMLAttributes))), \"color\" | \"onClick\" | \"rel\" | \"target\"> & { size?: \"m\" | \"s\" | \"l\" | \"xs\" | undefined; color?: \"text\" | \"subdued\" | \"primary\" | undefined; label: React.ReactNode; isActive?: boolean | undefined; isDisabled?: boolean | undefined; href?: string | undefined; target?: string | undefined; rel?: string | undefined; iconType?: ", "IconType", " | undefined; iconProps?: Omit<", "EuiIconProps", diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 054c3c84ee76e..bdd380df7d796 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 292 | 1 | 289 | 15 | +| 305 | 1 | 301 | 15 | ## Client diff --git a/api_docs/osquery.devdocs.json b/api_docs/osquery.devdocs.json index f03a3b873a0ca..e17d98b45ea42 100644 --- a/api_docs/osquery.devdocs.json +++ b/api_docs/osquery.devdocs.json @@ -301,7 +301,15 @@ "label": "createActionService", "description": [], "signature": [ - "{ create: (params: { 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; timeout?: number | 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) => Promise<{ response: { action_id: string; '@timestamp': string; expiration: string; type: string; input_type: string; alert_ids: string[] | undefined; event_ids: string[] | undefined; case_ids: string[] | undefined; agent_ids: string[] | undefined; agent_all: boolean | undefined; agent_platforms: string[] | undefined; agent_policy_ids: string[] | undefined; agents: string[]; user_id: string | undefined; metadata: object | undefined; pack_id: string | undefined; pack_name: string | undefined; pack_prebuilt: boolean | undefined; queries: ", + "{ create: (params: { 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; timeout?: number | 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) => Promise<{ response: { action_id: string; '@timestamp': string; expiration: string; type: string; input_type: string; alert_ids: string[] | undefined; event_ids: string[] | undefined; case_ids: string[] | undefined; agent_ids: string[] | undefined; agent_all: boolean | undefined; agent_platforms: string[] | undefined; agent_policy_ids: string[] | undefined; agents: string[]; user_id: string | undefined; metadata: object | undefined; pack_id: string | undefined; pack_name: string | undefined; pack_prebuilt: boolean | undefined; queries: ", "Dictionary", "[]; }; fleetActionsCount: number; }>; stop: () => void; }" ], diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 338089cad1cde..2566ca1190674 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index 37941b9751755..32c0d012ca887 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 216a7d190e689..63005d80ca9de 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-11-22 +date: 2023-11-30 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 | |--------------|----------|------------------------| -| 715 | 607 | 40 | +| 723 | 613 | 40 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 76428 | 235 | 65364 | 1605 | +| 76978 | 235 | 65681 | 1613 | ## Plugin Directory @@ -30,14 +30,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 269 | 0 | 263 | 31 | | | [@elastic/appex-sharedux @elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 17 | 1 | 15 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 70 | 1 | 4 | 1 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 810 | 1 | 779 | 50 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 813 | 1 | 782 | 51 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | The user interface for Elastic APM | 29 | 0 | 29 | 120 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 9 | 0 | 9 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Asset manager plugin for entity assets (inventory, topology, etc) | 9 | 0 | 9 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 104 | 0 | 84 | 28 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 115 | 0 | 95 | 28 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 268 | 2 | 253 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 72 | 0 | 16 | 0 | | cloudChat | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Chat available on Elastic Cloud deployments for quicker assistance. | 0 | 0 | 0 | 0 | @@ -62,9 +62,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 922 | 0 | 257 | 4 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 31 | 3 | 25 | 1 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of dataset quality, where users can easily get an overview on the datasets they have. | 4 | 0 | 4 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of dataset quality, where users can easily get an overview on the datasets they have. | 8 | 0 | 8 | 3 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 12 | 0 | 10 | 3 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 133 | 0 | 90 | 20 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 134 | 0 | 91 | 20 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Server APIs for the Elastic AI Assistant | 4 | 0 | 2 | 0 | @@ -96,7 +96,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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. | 59 | 0 | 59 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 0 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1212 | 3 | 1094 | 46 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1213 | 3 | 1095 | 47 | | 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 | @@ -118,7 +118,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [@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) | - | 610 | 3 | 417 | 9 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | - | 5 | 0 | 5 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 625 | 0 | 526 | 60 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 630 | 0 | 531 | 60 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -132,17 +132,18 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 60 | 0 | 60 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Exposes utilities for accessing metrics data | 104 | 8 | 104 | 4 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 150 | 3 | 64 | 33 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 25 | 0 | 19 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 15 | 3 | 13 | 1 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 35 | 0 | 35 | 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) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 580 | 2 | 571 | 17 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 581 | 2 | 572 | 17 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 42 | 0 | 39 | 7 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin exposes and registers observability log consumption features. | 15 | 0 | 15 | 1 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 14 | 0 | 14 | 0 | -| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 292 | 1 | 289 | 15 | +| | [@elastic/observability-ui](https://github.com/orgs/elastic/teams/observability-ui) | - | 305 | 1 | 301 | 15 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 7 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 2 | 0 | 2 | 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 | 2 | 164 | 11 | @@ -151,7 +152,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 23 | 0 | 23 | 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. | 22 | 0 | 6 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 21 | 0 | 21 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 267 | 0 | 238 | 14 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 268 | 0 | 239 | 14 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 24 | 0 | 19 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 129 | 2 | 118 | 4 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 25 | 0 | 25 | 0 | @@ -162,8 +163,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 13 | 0 | | | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 32 | 0 | 8 | 4 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 270 | 0 | 87 | 3 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 175 | 0 | 106 | 35 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 401 | 0 | 196 | 2 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 178 | 0 | 109 | 36 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | ESS customizations for Security Solution. | 6 | 0 | 6 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Serverless customizations for security. | 7 | 0 | 7 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | The core Serverless plugin, providing APIs to Serverless Project plugins. | 19 | 0 | 18 | 0 | @@ -181,13 +182,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 31 | 0 | 26 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 0 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 20 | 0 | 20 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 23 | 0 | 9 | 0 | | | [@elastic/protections-experience](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 30 | 0 | 14 | 5 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 240 | 1 | 196 | 17 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 583 | 1 | 557 | 55 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 145 | 0 | 103 | 9 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 135 | 0 | 93 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 212 | 0 | 145 | 10 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer). | 13 | 0 | 10 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 55 | 0 | 23 | 2 | @@ -211,7 +212,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the vislib visualizations. These are the classical area/line/bar, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 1 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 52 | 0 | 50 | 5 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 837 | 12 | 807 | 19 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 835 | 12 | 804 | 19 | | watcher | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -239,6 +240,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 10 | 0 | 10 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 7 | 0 | 7 | 1 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 16 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 60 | 1 | 42 | 3 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 14 | 0 | 10 | 0 | @@ -278,7 +280,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 20 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 165 | 0 | 70 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 166 | 0 | 70 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | @@ -327,7 +329,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 6 | 26 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 13 | 0 | 13 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 454 | 1 | 179 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 59 | 0 | 53 | 8 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 58 | 0 | 52 | 9 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 44 | 0 | 43 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | @@ -374,7 +376,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 25 | 1 | 24 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 111 | 1 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 352 | 1 | 5 | 2 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 353 | 1 | 5 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 89 | 0 | 61 | 10 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | @@ -445,11 +447,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 35125 | 0 | 34718 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 5 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 35 | 0 | 34 | 0 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 96 | 0 | 76 | 6 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 97 | 0 | 77 | 6 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 48 | 0 | 33 | 7 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 27 | 0 | 14 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 3 | 0 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 259 | 1 | 199 | 15 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 261 | 1 | 201 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 19 | 0 | 19 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | @@ -518,21 +520,23 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 8 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 1 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 29 | 0 | 29 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 7 | 0 | 7 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 29 | 0 | 27 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 18 | 0 | 18 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 31 | 1 | 24 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 78 | 0 | 76 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 1 | 50 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 13 | 0 | 13 | 3 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 99 | 1 | 99 | 0 | +| | [@elastic/security-detection-rule-management](https://github.com/orgs/elastic/teams/security-detection-rule-management) | - | 6 | 0 | 6 | 0 | | | [@elastic/security-detection-rule-management](https://github.com/orgs/elastic/teams/security-detection-rule-management) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 45 | 10 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 51 | 5 | 34 | 0 | | | [@elastic/security-asset-management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 66 | 0 | 66 | 0 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-performance-testing](https://github.com/orgs/elastic/teams/kibana-performance-testing) | - | 3 | 0 | 3 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 171 | 0 | 28 | 0 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 173 | 0 | 30 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 13 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 22 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 2 | 0 | @@ -560,9 +564,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 16 | 1 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 116 | 0 | 113 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 69 | 0 | 69 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 66 | 0 | 66 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 2211 | 0 | 2211 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 82 | 0 | 35 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 44 | 0 | 14 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 213 | 0 | 114 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 14 | 0 | 14 | 6 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 52 | 0 | 48 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 29 | 0 | 23 | 0 | @@ -572,7 +579,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 341 | 1 | 337 | 32 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 87 | 0 | 76 | 1 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 104 | 0 | 93 | 1 | -| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 17 | 0 | 12 | 7 | +| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 17 | 0 | 12 | 8 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 15 | 0 | 7 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 147 | 0 | 125 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 532 | 0 | 519 | 0 | @@ -584,8 +591,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 207 | 0 | 160 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 28 | 0 | 25 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 120 | 0 | 116 | 0 | -| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 37 | 0 | 32 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 54 | 0 | 51 | 1 | +| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 44 | 0 | 39 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 61 | 0 | 57 | 1 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 31 | 0 | 30 | 1 | | | [@elastic/appex-sharedux @elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 1 | 0 | 1 | 0 | | | [@elastic/appex-sharedux @elastic/platform-deployment-management @elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 1 | 0 | 1 | 0 | @@ -599,7 +606,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 26 | 0 | 8 | 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) | - | 60 | 0 | 49 | 5 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 61 | 0 | 48 | 5 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 2 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 5 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 2 | 0 | @@ -637,16 +644,15 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 102 | 2 | 65 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 4 | 0 | 2 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 21 | 0 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 24 | 0 | 16 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 5 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 291 | 4 | 244 | 12 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 137 | 5 | 105 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 22 | 0 | 21 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 25 | 0 | 10 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 72 | 0 | 55 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 39 | 0 | 25 | 1 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 86 | 0 | 86 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 49 | 0 | 35 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 44 | 0 | 30 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 53 | 0 | 44 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the unified data table which can be integrated into apps | 109 | 0 | 49 | 1 | @@ -658,7 +664,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 80 | 1 | 21 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 0 | 16 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 24 | 0 | 14 | 0 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 25 | 0 | 15 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 154 | 0 | 151 | 3 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 8899060750829..851cc51baacaf 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 08bd00e8dd034..5a4573b7d09e0 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index a2ff6f0d20702..864641e5cdfbb 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index f4769b4850d0c..13d2983c76f12 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-11-22 +date: 2023-11-30 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 d1bd21dbcb351..af9ce0ea01c62 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-11-22 +date: 2023-11-30 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 25675ffbea5d5..3b48d45347337 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-11-22 +date: 2023-11-30 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 6a8f4d39d6857..6e18cc13b1049 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -107,7 +107,15 @@ "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, @@ -403,7 +411,15 @@ "SortOptions", "[] | undefined; track_total_hits?: number | boolean | undefined; _source?: string[] | undefined; }) => Promise<", "SearchResponse", - ">, Record>, Record>>" ], @@ -626,7 +642,7 @@ "label": "getBrowserFields", "description": [], "signature": [ - "({ indices, metaFields, allowNoIndex, }: { indices: string[]; metaFields: string[]; allowNoIndex: boolean; }) => Promise<{ browserFields: ", + "({ featureIds, indices, metaFields, allowNoIndex, }: { featureIds: string[]; indices: string[]; metaFields: string[]; allowNoIndex: boolean; }) => Promise<{ browserFields: ", { "pluginId": "ruleRegistry", "scope": "common", @@ -653,12 +669,26 @@ "id": "def-server.AlertsClient.getBrowserFields.$1", "type": "Object", "tags": [], - "label": "{\n indices,\n metaFields,\n allowNoIndex,\n }", + "label": "{\n featureIds,\n indices,\n metaFields,\n allowNoIndex,\n }", "description": [], "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "ruleRegistry", + "id": "def-server.AlertsClient.getBrowserFields.$1.featureIds", + "type": "Array", + "tags": [], + "label": "featureIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "ruleRegistry", "id": "def-server.AlertsClient.getBrowserFields.$1.indices", @@ -1651,9 +1681,9 @@ }, ") => ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.AuditEvent", "text": "AuditEvent" } @@ -2701,7 +2731,15 @@ "signature": [ "> & OutputOf>>>(request: TSearchRequest) => Promise<", + ", TAlertDoc = Partial> & OutputOf>>>(request: TSearchRequest) => Promise<", { "pluginId": "@kbn/es-types", "scope": "common", @@ -3338,7 +3376,15 @@ "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, @@ -4752,7 +4798,15 @@ "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, diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 05a145ce8d7e3..5ab2c44a7756b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.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 | |-------------------|-----------|------------------------|-----------------| -| 267 | 0 | 238 | 14 | +| 268 | 0 | 239 | 14 | ## Server diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index a2c16d045966d..aa5cd33093911 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-11-22 +date: 2023-11-30 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 e603853980878..4bc7c38896edd 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-11-22 +date: 2023-11-30 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 998cb89468652..1a11fb5649197 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-11-22 +date: 2023-11-30 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 0c087c56c37e4..112ad59aab742 100644 --- a/api_docs/saved_objects_management.devdocs.json +++ b/api_docs/saved_objects_management.devdocs.json @@ -294,7 +294,7 @@ "label": "euiColumn", "description": [], "signature": [ - "{ prefix?: string | undefined; scope?: string | undefined; id?: string | undefined; defaultValue?: string | number | readonly string[] | undefined; name: React.ReactNode; security?: string | undefined; children?: React.ReactNode; description?: string | undefined; onChange?: React.FormEventHandler | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; className?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; title?: string | undefined; translate?: \"no\" | \"yes\" | 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; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"url\" | \"email\" | \"tel\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"both\" | \"inline\" | 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\" | \"location\" | \"true\" | \"false\" | \"time\" | \"step\" | 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 | \"true\" | \"false\" | \"grid\" | \"menu\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"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\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; 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; 'data-test-subj'?: string | undefined; css?: ", + "{ prefix?: string | undefined; scope?: string | undefined; id?: string | undefined; defaultValue?: string | number | readonly string[] | undefined; name: React.ReactNode; security?: string | undefined; children?: React.ReactNode; description?: string | undefined; onChange?: React.FormEventHandler | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; className?: string | undefined; contentEditable?: Booleanish | \"inherit\" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; lang?: string | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; title?: string | 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; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"search\" | \"none\" | \"text\" | \"url\" | \"email\" | \"tel\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"both\" | \"inline\" | 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\" | \"location\" | \"true\" | \"false\" | \"time\" | \"step\" | 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 | \"true\" | \"false\" | \"grid\" | \"menu\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"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\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; 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; 'data-test-subj'?: string | undefined; css?: ", "Interpolation", "<", "Theme", @@ -306,9 +306,9 @@ "section": "def-public.SavedObjectsManagementRecord", "text": "SavedObjectsManagementRecord" }, - "; width?: string | undefined; headers?: string | undefined; dataType?: ", - "EuiTableDataType", - " | undefined; render?: ((value: any, record: ", + "; width?: string | undefined; headers?: string | undefined; abbr?: string | undefined; footer?: string | React.ReactElement> | ((props: ", + "EuiTableFooterProps", + "<", { "pluginId": "savedObjectsManagement", "scope": "public", @@ -316,11 +316,9 @@ "section": "def-public.SavedObjectsManagementRecord", "text": "SavedObjectsManagementRecord" }, - ") => React.ReactNode) | undefined; height?: string | number | undefined; align?: ", - "HorizontalAlignment", - " | undefined; abbr?: string | undefined; footer?: string | React.ReactElement> | ((props: ", - "EuiTableFooterProps", - "<", + ">) => React.ReactNode) | undefined; dataType?: ", + "EuiTableDataType", + " | undefined; render?: ((value: any, record: ", { "pluginId": "savedObjectsManagement", "scope": "public", @@ -328,7 +326,9 @@ "section": "def-public.SavedObjectsManagementRecord", "text": "SavedObjectsManagementRecord" }, - ">) => React.ReactNode) | undefined; readOnly?: boolean | undefined; colSpan?: number | undefined; rowSpan?: number | undefined; valign?: \"top\" | \"bottom\" | \"middle\" | \"baseline\" | undefined; isExpander?: boolean | undefined; textOnly?: boolean | undefined; truncateText?: boolean | { lines: number; } | undefined; mobileOptions?: (Omit<", + ") => React.ReactNode) | undefined; height?: string | number | undefined; align?: ", + "HorizontalAlignment", + " | undefined; readOnly?: boolean | undefined; colSpan?: number | undefined; rowSpan?: number | undefined; valign?: \"top\" | \"bottom\" | \"middle\" | \"baseline\" | undefined; isExpander?: boolean | undefined; textOnly?: boolean | undefined; truncateText?: boolean | { lines: number; } | undefined; mobileOptions?: (Omit<", "EuiTableRowCellMobileOptionsShape", ", \"render\"> & { render?: ((item: ", { diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 7655c2a5d6819..65f414faecbee 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-11-22 +date: 2023-11-30 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 e2c9953647258..aaffc31371dde 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-11-22 +date: 2023-11-30 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 a9bed4e631b9b..0f80cd7516aac 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-11-22 +date: 2023-11-30 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 70e1f310d8e6d..9699f1d3a77c0 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-11-22 +date: 2023-11-30 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 4d1dcd45c7253..f7ee5a283c576 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-11-22 +date: 2023-11-30 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 685728394a013..fb73fe558213b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index f948514fa2fae..d29eac4beaa07 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -15,22 +15,22 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, " extends ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.User", "text": "User" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -45,14 +45,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserRealm", "text": "UserRealm" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -67,14 +67,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserRealm", "text": "UserRealm" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -89,14 +89,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticationProvider", "text": "AuthenticationProvider" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -109,7 +109,7 @@ "description": [ "\nThe AuthenticationType used by ES to authenticate the user.\n" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -122,7 +122,7 @@ "description": [ "\nIndicates whether user is authenticated via Elastic Cloud built-in SAML realm." ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -138,7 +138,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false } @@ -152,7 +152,7 @@ "tags": [], "label": "AuthenticationServiceSetup", "description": [], - "path": "x-pack/plugins/security/public/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -168,15 +168,15 @@ "signature": [ "() => Promise<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, ">" ], - "path": "x-pack/plugins/security/public/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -194,7 +194,7 @@ "signature": [ "() => Promise" ], - "path": "x-pack/plugins/security/public/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -223,9 +223,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" } @@ -257,9 +257,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" } @@ -278,7 +278,7 @@ "tags": [], "label": "SecurityLicense", "description": [], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -292,7 +292,7 @@ "signature": [ "() => boolean" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -308,7 +308,7 @@ "signature": [ "() => boolean" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -324,14 +324,14 @@ "signature": [ "() => ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.SecurityLicenseFeatures", "text": "SecurityLicenseFeatures" } ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -347,7 +347,7 @@ "signature": [ "(licenseType: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\") => boolean | undefined" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -361,7 +361,7 @@ "signature": [ "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -380,15 +380,15 @@ "Observable", "<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.SecurityLicenseFeatures", "text": "SecurityLicenseFeatures" }, ">" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false } @@ -404,7 +404,7 @@ "description": [ "\nDescribes Security plugin features that depend on license." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -417,7 +417,7 @@ "description": [ "\nIndicates whether we show login page or skip it." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -430,7 +430,7 @@ "description": [ "\nIndicates whether we allow login or disable it on the login page." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -443,7 +443,7 @@ "description": [ "\nIndicates whether we show security links throughout the kibana app." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -456,7 +456,7 @@ "description": [ "\nIndicates whether we show the Role Mappings UI." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -469,7 +469,7 @@ "description": [ "\nIndicates whether we allow users to access agreement UI and acknowledge it." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -482,7 +482,7 @@ "description": [ "\nIndicates whether we allow logging of audit events." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -495,7 +495,7 @@ "description": [ "\nIndicates whether we allow users to define document level security in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -508,7 +508,7 @@ "description": [ "\nIndicates whether we allow users to define field level security in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -521,7 +521,7 @@ "description": [ "\nIndicates whether we allow users to define remote index privileges in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -534,7 +534,7 @@ "description": [ "\nIndicates whether we allow Role-based access control (RBAC)." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -547,7 +547,7 @@ "description": [ "\nIndicates whether we allow sub-feature privileges." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -560,7 +560,7 @@ "description": [ "\nIndicates whether we allow user profile collaboration features (suggest and privileges checks APIs)." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -575,15 +575,15 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.LoginLayout", "text": "LoginLayout" }, " | undefined" ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false } @@ -597,7 +597,7 @@ "tags": [], "label": "SecurityNavControlServiceStart", "description": [], - "path": "x-pack/plugins/security/public/nav_control/nav_control_service.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -615,15 +615,15 @@ "Observable", "<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-public", "scope": "public", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", "section": "def-public.UserMenuLink", "text": "UserMenuLink" }, "[]>" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_service.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -641,15 +641,15 @@ "signature": [ "(newUserMenuLink: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-public", "scope": "public", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", "section": "def-public.UserMenuLink", "text": "UserMenuLink" }, "[]) => void" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_service.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -662,15 +662,15 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-public", "scope": "public", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", "section": "def-public.UserMenuLink", "text": "UserMenuLink" }, "[]" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_service.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -732,7 +732,7 @@ "tags": [], "label": "UserMenuLink", "description": [], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -743,7 +743,7 @@ "tags": [], "label": "label", "description": [], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false }, @@ -757,7 +757,7 @@ "signature": [ "string | React.ComponentType<{}>" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false }, @@ -768,7 +768,7 @@ "tags": [], "label": "href", "description": [], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false }, @@ -782,7 +782,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false }, @@ -796,7 +796,7 @@ "signature": [ "boolean | undefined" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false }, @@ -812,7 +812,7 @@ "signature": [ "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" ], - "path": "x-pack/plugins/security/public/nav_control/nav_control_component.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts", "deprecated": false, "trackAdoption": false } @@ -828,7 +828,7 @@ "description": [ "\nParameters for the bulk get API." ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -844,7 +844,7 @@ "signature": [ "Set" ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false }, @@ -860,7 +860,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false } @@ -876,7 +876,7 @@ "description": [ "\nParameters for the get user profile for the current user API." ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -889,7 +889,7 @@ "description": [ "\nBy default, get API returns user information, but does not return any user data. The optional \"dataPath\"\nparameter can be used to return personal data for this user (within `kibana` namespace only)." ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false } @@ -905,7 +905,7 @@ "description": [ "\nParameters for the suggest API." ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -918,7 +918,7 @@ "description": [ "\nQuery string used to match name-related fields in user profiles. The following fields are treated as\nname-related: username, full_name and email." ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false }, @@ -934,7 +934,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false }, @@ -950,7 +950,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts", + "path": "x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts", "deprecated": false, "trackAdoption": false } @@ -988,115 +988,108 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-public", "scope": "public", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", "section": "def-public.AuthenticationServiceSetup", "text": "AuthenticationServiceSetup" } ], - "path": "x-pack/plugins/security/public/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false } ], "objects": [], - "setup": { + "start": { "parentPluginId": "security", - "id": "def-public.SecurityPluginSetup", + "id": "def-public.SecurityPluginStart", "type": "Interface", "tags": [], - "label": "SecurityPluginSetup", + "label": "SecurityPluginStart", "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "public", + "docId": "kibSecurityPluginApi", + "section": "def-public.SecurityPluginStart", + "text": "SecurityPluginStart" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-public", + "scope": "public", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", + "section": "def-public.SecurityPluginStart", + "text": "SecurityPluginStart" + } + ], "path": "x-pack/plugins/security/public/plugin.tsx", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-public.SecurityPluginSetup.authc", + "id": "def-public.SecurityPluginStart.uiApi", "type": "Object", - "tags": [], - "label": "authc", + "tags": [ + "deprecated" + ], + "label": "uiApi", "description": [ - "\nExposes authentication information about the currently logged in user." + "\nExposes UI components that will be loaded asynchronously." ], "signature": [ { "pluginId": "security", "scope": "public", "docId": "kibSecurityPluginApi", - "section": "def-public.AuthenticationServiceSetup", - "text": "AuthenticationServiceSetup" + "section": "def-public.UiApi", + "text": "UiApi" } ], "path": "x-pack/plugins/security/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-public.SecurityPluginSetup.license", - "type": "Object", - "tags": [], - "label": "license", - "description": [ - "\nExposes information about the available security features under the current license." - ], - "signature": [ + "deprecated": true, + "trackAdoption": false, + "references": [ { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.SecurityLicense", - "text": "SecurityLicense" + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" } - ], - "path": "x-pack/plugins/security/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false + ] } ], - "lifecycle": "setup", + "lifecycle": "start", "initialIsOpen": true }, - "start": { + "setup": { "parentPluginId": "security", - "id": "def-public.SecurityPluginStart", + "id": "def-public.SecurityPluginSetup", "type": "Interface", "tags": [], - "label": "SecurityPluginStart", + "label": "SecurityPluginSetup", "description": [], - "path": "x-pack/plugins/security/public/plugin.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-public.SecurityPluginStart.navControlService", - "type": "Object", - "tags": [], - "label": "navControlService", - "description": [ - "\nExposes the ability to add custom links to the dropdown menu in the top right, where the user's Avatar is." - ], - "signature": [ - { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.SecurityNavControlServiceStart", - "text": "SecurityNavControlServiceStart" - } - ], - "path": "x-pack/plugins/security/public/plugin.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-public.SecurityPluginStart.authc", + "id": "def-public.SecurityPluginSetup.authc", "type": "Object", "tags": [], "label": "authc", @@ -1105,167 +1098,41 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-public", "scope": "public", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesPublicPluginApi", "section": "def-public.AuthenticationServiceSetup", "text": "AuthenticationServiceSetup" } ], - "path": "x-pack/plugins/security/public/plugin.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-public.SecurityPluginStart.userProfiles", + "id": "def-public.SecurityPluginSetup.license", "type": "Object", "tags": [], - "label": "userProfiles", + "label": "license", "description": [ - "\nA set of methods to work with Kibana user profiles." + "\nExposes information about the available security features under the current license." ], "signature": [ - "{ update: (data: D) => Promise; suggest: (path: string, params: ", - { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.UserProfileSuggestParams", - "text": "UserProfileSuggestParams" - }, - ") => Promise<", - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.UserProfile", - "text": "UserProfile" - }, - "[]>; bulkGet: (params: ", - { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.UserProfileBulkGetParams", - "text": "UserProfileBulkGetParams" - }, - ") => Promise<", - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.UserProfile", - "text": "UserProfile" - }, - "[]>; getCurrent: (params?: ", - { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.UserProfileGetCurrentParams", - "text": "UserProfileGetCurrentParams" - }, - " | undefined) => Promise<", - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.GetUserProfileResponse", - "text": "GetUserProfileResponse" - }, - ">; readonly userProfile$: ", - "Observable", - "<", { - "pluginId": "@kbn/user-profile-components", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibKbnUserProfileComponentsPluginApi", - "section": "def-common.UserProfileData", - "text": "UserProfileData" - }, - " | null>; }" + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.SecurityLicense", + "text": "SecurityLicense" + } ], - "path": "x-pack/plugins/security/public/plugin.tsx", + "path": "x-pack/packages/security/plugin_types_public/src/plugin.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-public.SecurityPluginStart.uiApi", - "type": "Object", - "tags": [ - "deprecated" - ], - "label": "uiApi", - "description": [ - "\nExposes UI components that will be loaded asynchronously." - ], - "signature": [ - { - "pluginId": "security", - "scope": "public", - "docId": "kibSecurityPluginApi", - "section": "def-public.UiApi", - "text": "UiApi" - } - ], - "path": "x-pack/plugins/security/public/plugin.tsx", - "deprecated": true, - "trackAdoption": false, - "references": [ - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" - }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" - }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" - }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx" - } - ] } ], - "lifecycle": "start", + "lifecycle": "setup", "initialIsOpen": true } }, @@ -1438,338 +1305,165 @@ "interfaces": [ { "parentPluginId": "security", - "id": "def-server.AuditEvent", + "id": "def-server.Actions", "type": "Interface", "tags": [], - "label": "AuditEvent", + "label": "Actions", "description": [ - "\nAudit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html\n\nIf you add additional fields to the schema ensure you update the Kibana Filebeat module:\nhttps://github.com/elastic/beats/tree/master/filebeat/module/kibana\n" - ], - "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditEvent", - "text": "AuditEvent" - }, - " extends ", - { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.LogMeta", - "text": "LogMeta" - } + "Actions are used to create the \"actions\" that are associated with Elasticsearch's\napplication privileges, and are used to perform the authorization checks implemented\nby the various `checkPrivilegesWithRequest` derivatives." ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditEvent.message", - "type": "string", + "id": "def-server.Actions.api", + "type": "Object", "tags": [], - "label": "message", - "description": [ - "\nLog message" + "label": "api", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ApiActions", + "text": "ApiActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditEvent.kibana", + "id": "def-server.Actions.app", "type": "Object", "tags": [], - "label": "kibana", - "description": [ - "\nKibana specific fields" - ], + "label": "app", + "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditKibana", - "text": "AuditKibana" - }, - " | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditEvent.http", - "type": "Object", - "tags": [], - "label": "http", - "description": [ - "\nFields describing an HTTP request" - ], - "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditHttp", - "text": "AuditHttp" - }, - " | undefined" + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AppActions", + "text": "AppActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditHttp", - "type": "Interface", - "tags": [], - "label": "AuditHttp", - "description": [ - "\nAudit http schema using ECS format" - ], - "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditHttp", - "text": "AuditHttp" }, - " extends ", - { - "pluginId": "@kbn/ecs", - "scope": "common", - "docId": "kibKbnEcsPluginApi", - "section": "def-common.EcsHttp", - "text": "EcsHttp" - } - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { "parentPluginId": "security", - "id": "def-server.AuditHttp.request", + "id": "def-server.Actions.cases", "type": "Object", "tags": [], - "label": "request", - "description": [ - "\nHTTP request details" - ], + "label": "cases", + "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditRequest", - "text": "AuditRequest" - }, - " | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana", - "type": "Interface", - "tags": [], - "label": "AuditKibana", - "description": [ - "\nAudit kibana schema using ECS format" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.space_id", - "type": "string", - "tags": [], - "label": "space_id", - "description": [ - "\nThe ID of the space associated with this event." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.session_id", - "type": "string", - "tags": [], - "label": "session_id", - "description": [ - "\nThe ID of the user session associated with this event. Each login attempt\nresults in a unique session id." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.saved_object", - "type": "Object", - "tags": [], - "label": "saved_object", - "description": [ - "\nSaved object that was created, changed, deleted or accessed as part of this event." - ], - "signature": [ - "{ type: string; id: string; } | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.authentication_provider", - "type": "string", - "tags": [], - "label": "authentication_provider", - "description": [ - "\nName of authentication provider associated with a login event." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.authentication_type", - "type": "string", - "tags": [], - "label": "authentication_type", - "description": [ - "\nType of authentication provider associated with a login event." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuditKibana.authentication_realm", - "type": "string", - "tags": [], - "label": "authentication_realm", - "description": [ - "\nName of Elasticsearch realm that has authenticated the user." - ], - "signature": [ - "string | undefined" + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CasesActions", + "text": "CasesActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditKibana.lookup_realm", + "id": "def-server.Actions.login", "type": "string", "tags": [], - "label": "lookup_realm", - "description": [ - "\nName of Elasticsearch realm where the user details were retrieved from." - ], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "label": "login", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditKibana.add_to_spaces", + "id": "def-server.Actions.savedObject", "type": "Object", "tags": [], - "label": "add_to_spaces", - "description": [ - "\nSet of space IDs that a saved object was shared to." - ], + "label": "savedObject", + "description": [], "signature": [ - "readonly string[] | undefined" + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.SavedObjectActions", + "text": "SavedObjectActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditKibana.delete_from_spaces", + "id": "def-server.Actions.alerting", "type": "Object", "tags": [], - "label": "delete_from_spaces", - "description": [ - "\nSet of space IDs that a saved object was removed from." - ], + "label": "alerting", + "description": [], "signature": [ - "readonly string[] | undefined" + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AlertingActions", + "text": "AlertingActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditKibana.unauthorized_spaces", + "id": "def-server.Actions.space", "type": "Object", "tags": [], - "label": "unauthorized_spaces", - "description": [ - "\nSet of space IDs that are not authorized for an action." - ], + "label": "space", + "description": [], "signature": [ - "readonly string[] | undefined" + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.SpaceActions", + "text": "SpaceActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuditKibana.unauthorized_types", + "id": "def-server.Actions.ui", "type": "Object", "tags": [], - "label": "unauthorized_types", - "description": [ - "\nSet of types that are not authorized for an action." - ], + "label": "ui", + "description": [], "signature": [ - "readonly string[] | undefined" + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UIActions", + "text": "UIActions" + } ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts", "deprecated": false, "trackAdoption": false } @@ -1778,141 +1472,196 @@ }, { "parentPluginId": "security", - "id": "def-server.AuditLogger", + "id": "def-server.AlertingActions", "type": "Interface", "tags": [], - "label": "AuditLogger", + "label": "AlertingActions", "description": [], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditLogger.log", + "id": "def-server.AlertingActions.get", "type": "Function", "tags": [], - "label": "log", - "description": [ - "\nLogs an {@link AuditEvent} and automatically adds meta data about the\ncurrent user, space and correlation id.\n\nGuidelines around what events should be logged and how they should be\nstructured can be found in: `/x-pack/plugins/security/README.md`\n" - ], + "label": "get", + "description": [], "signature": [ - "(event: ", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditEvent", - "text": "AuditEvent" - }, - " | undefined) => void" + "(ruleTypeId: string, consumer: string, alertingEntity: string, operation: string) => string" ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditLogger.log.$1", - "type": "Object", + "id": "def-server.AlertingActions.get.$1", + "type": "string", "tags": [], - "label": "event", + "label": "ruleTypeId", "description": [], "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditEvent", - "text": "AuditEvent" - }, - " | undefined" + "string" ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", "deprecated": false, "trackAdoption": false, - "isRequired": false + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.AlertingActions.get.$2", + "type": "string", + "tags": [], + "label": "consumer", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.AlertingActions.get.$3", + "type": "string", + "tags": [], + "label": "alertingEntity", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.AlertingActions.get.$4", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] - }, - { - "parentPluginId": "security", - "id": "def-server.AuditLogger.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [ - "\nIndicates whether audit logging is enabled or not.\n\nUseful for skipping resource-intense operations that don't need to be performed when audit\nlogging is disabled." - ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "security", - "id": "def-server.AuditRequest", + "id": "def-server.ApiActions", "type": "Interface", "tags": [], - "label": "AuditRequest", - "description": [ - "\nAudit request schema using ECS format" - ], - "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditRequest", - "text": "AuditRequest" - }, - " extends { body?: { bytes?: number | undefined; content?: string | undefined; } | undefined; bytes?: number | undefined; id?: string | undefined; method?: string | undefined; mime_type?: string | undefined; referrer?: string | undefined; }" - ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "label": "ApiActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditRequest.headers", - "type": "Object", + "id": "def-server.ApiActions.get", + "type": "Function", "tags": [], - "label": "headers", - "description": [ - "\nHTTP request headers" - ], + "label": "get", + "description": [], "signature": [ - "{ 'x-forwarded-for'?: string | undefined; } | undefined" + "(operation: string) => string" ], - "path": "x-pack/plugins/security/server/audit/audit_events.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.ApiActions.get.$1", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false }, { "parentPluginId": "security", - "id": "def-server.AuditServiceSetup", + "id": "def-server.APIKeys", "type": "Interface", "tags": [], - "label": "AuditServiceSetup", + "label": "APIKeys", "description": [], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditServiceSetup.asScoped", + "id": "def-server.APIKeys.areAPIKeysEnabled", "type": "Function", "tags": [], - "label": "asScoped", + "label": "areAPIKeysEnabled", "description": [ - "\nCreates an {@link AuditLogger} scoped to the current request.\n\nThis audit logger logs events with all required user and session info and should be used for\nall user-initiated actions.\n" + "\nDetermines if API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.areCrossClusterAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areCrossClusterAPIKeysEnabled", + "description": [ + "\nDetermines if Cross-Cluster API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nTries to create an API key for the current user.\n\nReturns newly created API key or `null` if API keys are disabled.\n\nUser needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.\n" ], "signature": [ "(request: ", @@ -1923,26 +1672,31 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ") => ", + ", createParams: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditLogger", - "text": "AuditLogger" - } + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + }, + ") => Promise<", + "SecurityCreateApiKeyResponse", + " | null>" ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuditServiceSetup.asScoped.$1", + "id": "def-server.APIKeys.create.$1", "type": "Object", "tags": [], "label": "request", - "description": [], + "description": [ + "Request instance." + ], "signature": [ { "pluginId": "@kbn/core-http-server", @@ -1953,7 +1707,30 @@ }, "" ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.create.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "The params to create an API key" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -1963,164 +1740,1868 @@ }, { "parentPluginId": "security", - "id": "def-server.AuditServiceSetup.withoutRequest", - "type": "Object", + "id": "def-server.APIKeys.grantAsInternalUser", + "type": "Function", "tags": [], - "label": "withoutRequest", + "label": "grantAsInternalUser", "description": [ - "\n{@link AuditLogger} for background tasks only.\n\nThis audit logger logs events without any user or session info and should never be used to log\nuser-initiated actions.\n" + "\nTries to grant an API key for the current user." ], "signature": [ + "(request: ", { - "pluginId": "security", + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditLogger", - "text": "AuditLogger" - } + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.GrantAPIKeyResult", + "text": "GrantAPIKeyResult" + }, + " | null>" ], - "path": "x-pack/plugins/security/server/audit/audit_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuthenticatedUser", - "type": "Interface", - "tags": [], - "label": "AuthenticatedUser", - "description": [ - "\nRepresents the currently authenticated user." - ], - "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.AuthenticatedUser", - "text": "AuthenticatedUser" + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.APIKeys.grantAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.grantAsInternalUser.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "Create operation parameters." + ], + "signature": [ + "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] }, - " extends ", { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.User", - "text": "User" + "parentPluginId": "security", + "id": "def-server.APIKeys.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [ + "\nTries to validate an API key." + ], + "signature": [ + "(apiKeyPrams: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + }, + ") => Promise" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.APIKeys.validate.$1", + "type": "Object", + "tags": [], + "label": "apiKeyPrams", + "description": [ + "ValidateAPIKeyParams." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.invalidate", + "type": "Function", + "tags": [], + "label": "invalidate", + "description": [ + "\nTries to invalidate an API keys." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.APIKeys.invalidate.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.invalidate.$2", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate an API keys." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.invalidateAsInternalUser", + "type": "Function", + "tags": [], + "label": "invalidateAsInternalUser", + "description": [ + "\nTries to invalidate the API keys by using the internal user." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.APIKeys.invalidateAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate the API keys." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AppActions", + "type": "Interface", + "tags": [], + "label": "AppActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AppActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AppActions.get.$1", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditEvent", + "type": "Interface", + "tags": [], + "label": "AuditEvent", + "description": [ + "\nAudit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html\n\nIf you add additional fields to the schema ensure you update the Kibana Filebeat module:\nhttps://github.com/elastic/beats/tree/master/filebeat/module/kibana\n" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " extends ", + { + "pluginId": "@kbn/logging", + "scope": "common", + "docId": "kibKbnLoggingPluginApi", + "section": "def-common.LogMeta", + "text": "LogMeta" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditEvent.message", + "type": "string", + "tags": [], + "label": "message", + "description": [ + "\nLog message" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditEvent.kibana", + "type": "Object", + "tags": [], + "label": "kibana", + "description": [ + "\nKibana specific fields" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditKibana", + "text": "AuditKibana" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditEvent.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [ + "\nFields describing an HTTP request" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditHttp", + "text": "AuditHttp" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditHttp", + "type": "Interface", + "tags": [], + "label": "AuditHttp", + "description": [ + "\nAudit http schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditHttp", + "text": "AuditHttp" + }, + " extends ", + { + "pluginId": "@kbn/ecs", + "scope": "common", + "docId": "kibKbnEcsPluginApi", + "section": "def-common.EcsHttp", + "text": "EcsHttp" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditHttp.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "\nHTTP request details" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditRequest", + "text": "AuditRequest" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana", + "type": "Interface", + "tags": [], + "label": "AuditKibana", + "description": [ + "\nAudit kibana schema using ECS format" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.space_id", + "type": "string", + "tags": [], + "label": "space_id", + "description": [ + "\nThe ID of the space associated with this event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.session_id", + "type": "string", + "tags": [], + "label": "session_id", + "description": [ + "\nThe ID of the user session associated with this event. Each login attempt\nresults in a unique session id." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.saved_object", + "type": "Object", + "tags": [], + "label": "saved_object", + "description": [ + "\nSaved object that was created, changed, deleted or accessed as part of this event." + ], + "signature": [ + "{ type: string; id: string; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.authentication_provider", + "type": "string", + "tags": [], + "label": "authentication_provider", + "description": [ + "\nName of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.authentication_type", + "type": "string", + "tags": [], + "label": "authentication_type", + "description": [ + "\nType of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.authentication_realm", + "type": "string", + "tags": [], + "label": "authentication_realm", + "description": [ + "\nName of Elasticsearch realm that has authenticated the user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.lookup_realm", + "type": "string", + "tags": [], + "label": "lookup_realm", + "description": [ + "\nName of Elasticsearch realm where the user details were retrieved from." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.add_to_spaces", + "type": "Object", + "tags": [], + "label": "add_to_spaces", + "description": [ + "\nSet of space IDs that a saved object was shared to." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.delete_from_spaces", + "type": "Object", + "tags": [], + "label": "delete_from_spaces", + "description": [ + "\nSet of space IDs that a saved object was removed from." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.unauthorized_spaces", + "type": "Object", + "tags": [], + "label": "unauthorized_spaces", + "description": [ + "\nSet of space IDs that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditKibana.unauthorized_types", + "type": "Object", + "tags": [], + "label": "unauthorized_types", + "description": [ + "\nSet of types that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditLogger", + "type": "Interface", + "tags": [], + "label": "AuditLogger", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditLogger.log", + "type": "Function", + "tags": [], + "label": "log", + "description": [ + "\nLogs an {@link AuditEvent} and automatically adds meta data about the\ncurrent user, space and correlation id.\n\nGuidelines around what events should be logged and how they should be\nstructured can be found in: `/x-pack/plugins/security/README.md`\n" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " | undefined) => void" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditLogger.log.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditEvent", + "text": "AuditEvent" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.AuditLogger.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nIndicates whether audit logging is enabled or not.\n\nUseful for skipping resource-intense operations that don't need to be performed when audit\nlogging is disabled." + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditRequest", + "type": "Interface", + "tags": [], + "label": "AuditRequest", + "description": [ + "\nAudit request schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditRequest", + "text": "AuditRequest" + }, + " extends { body?: { bytes?: number | undefined; content?: string | undefined; } | undefined; bytes?: number | undefined; id?: string | undefined; method?: string | undefined; mime_type?: string | undefined; referrer?: string | undefined; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditRequest.headers", + "type": "Object", + "tags": [], + "label": "headers", + "description": [ + "\nHTTP request headers" + ], + "signature": [ + "{ 'x-forwarded-for'?: string | undefined; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuditServiceSetup", + "type": "Interface", + "tags": [], + "label": "AuditServiceSetup", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditServiceSetup.asScoped", + "type": "Function", + "tags": [], + "label": "asScoped", + "description": [ + "\nCreates an {@link AuditLogger} scoped to the current request.\n\nThis audit logger logs events with all required user and session info and should be used for\nall user-initiated actions.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuditServiceSetup.asScoped.$1", + "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/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.AuditServiceSetup.withoutRequest", + "type": "Object", + "tags": [], + "label": "withoutRequest", + "description": [ + "\n{@link AuditLogger} for background tasks only.\n\nThis audit logger logs events without any user or session info and should never be used to log\nuser-initiated actions.\n" + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser", + "type": "Interface", + "tags": [], + "label": "AuthenticatedUser", + "description": [ + "\nRepresents the currently authenticated user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.User", + "text": "User" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.authentication_realm", + "type": "Object", + "tags": [], + "label": "authentication_realm", + "description": [ + "\nThe name and type of the Realm that has authenticated the user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserRealm", + "text": "UserRealm" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.lookup_realm", + "type": "Object", + "tags": [], + "label": "lookup_realm", + "description": [ + "\nThe name and type of the Realm where the user information were retrieved from." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.UserRealm", + "text": "UserRealm" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.authentication_provider", + "type": "Object", + "tags": [], + "label": "authentication_provider", + "description": [ + "\nThe authentication provider that used to authenticate user." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticationProvider", + "text": "AuthenticationProvider" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.authentication_type", + "type": "string", + "tags": [], + "label": "authentication_type", + "description": [ + "\nThe AuthenticationType used by ES to authenticate the user.\n" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.elastic_cloud_user", + "type": "boolean", + "tags": [], + "label": "elastic_cloud_user", + "description": [ + "\nIndicates whether user is authenticated via Elastic Cloud built-in SAML realm." + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticatedUser.profile_uid", + "type": "string", + "tags": [], + "label": "profile_uid", + "description": [ + "\nUser profile ID of this user." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticationServiceStart", + "type": "Interface", + "tags": [], + "label": "AuthenticationServiceStart", + "description": [ + "\nAuthentication services available on the security plugin's start contract." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthenticationServiceStart.apiKeys", + "type": "Object", + "tags": [], + "label": "apiKeys", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.APIKeys", + "text": "APIKeys" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthenticationServiceStart.getCurrentUser", + "type": "Function", + "tags": [], + "label": "getCurrentUser", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthenticationServiceStart.getCurrentUser.$1", + "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/packages/security/plugin_types_server/src/authentication/authentication_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationMode", + "type": "Interface", + "tags": [], + "label": "AuthorizationMode", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationMode.useRbacForRequest", + "type": "Function", + "tags": [], + "label": "useRbacForRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => boolean" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationMode.useRbacForRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/mode.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup", + "type": "Interface", + "tags": [], + "label": "AuthorizationServiceSetup", + "description": [ + "\nAuthorization services available on the setup contract of the security plugin." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [ + "\nActions are used to create the \"actions\" that are associated with Elasticsearch's\napplication privileges, and are used to perform the authorization checks implemented\nby the various `checkPrivilegesWithRequest` derivatives." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.Actions", + "text": "Actions" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesWithRequest", + "type": "Function", + "tags": [], + "label": "checkPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivileges", + "text": "CheckPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesDynamicallyWithRequest", + "type": "Function", + "tags": [], + "label": "checkPrivilegesDynamicallyWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesDynamically", + "text": "CheckPrivilegesDynamically" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkPrivilegesDynamicallyWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkSavedObjectsPrivilegesWithRequest", + "type": "Function", + "tags": [], + "label": "checkSavedObjectsPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckSavedObjectsPrivileges", + "text": "CheckSavedObjectsPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.checkSavedObjectsPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "security", + "id": "def-server.AuthorizationServiceSetup.mode", + "type": "Object", + "tags": [], + "label": "mode", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.AuthorizationMode", + "text": "AuthorizationMode" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CasesActions", + "type": "Interface", + "tags": [], + "label": "CasesActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CasesActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(owner: string, operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CasesActions.get.$1", + "type": "string", + "tags": [], + "label": "owner", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CasesActions.get.$2", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges", + "type": "Interface", + "tags": [], + "label": "CheckPrivileges", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpace", + "type": "Function", + "tags": [], + "label": "atSpace", + "description": [], + "signature": [ + "(spaceId: string, privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpace.$1", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpace.$2", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpace.$3", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpaces", + "type": "Function", + "tags": [], + "label": "atSpaces", + "description": [], + "signature": [ + "(spaceIds: string[], privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpaces.$1", + "type": "Array", + "tags": [], + "label": "spaceIds", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpaces.$2", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.atSpaces.$3", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.globally", + "type": "Function", + "tags": [], + "label": "globally", + "description": [], + "signature": [ + "(privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.globally.$1", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivileges.globally.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesOptions", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesOptions", + "description": [ + "\nOptions to influce the privilege checks." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.authentication_realm", - "type": "Object", + "id": "def-server.CheckPrivilegesOptions.requireLoginAction", + "type": "CompoundType", "tags": [], - "label": "authentication_realm", + "label": "requireLoginAction", "description": [ - "\nThe name and type of the Realm that has authenticated the user." + "\nWhether or not the `login` action should be required (default: true).\nSetting this to false is not advised except for special circumstances, when you do not require\nthe request to belong to a user capable of logging into Kibana." ], "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.UserRealm", - "text": "UserRealm" - } + "boolean | undefined" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesPayload", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesPayload", + "description": [ + "\nPrivileges that can be checked for the Kibana users." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.lookup_realm", - "type": "Object", + "id": "def-server.CheckPrivilegesPayload.kibana", + "type": "CompoundType", "tags": [], - "label": "lookup_realm", + "label": "kibana", "description": [ - "\nThe name and type of the Realm where the user information were retrieved from." + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." ], "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.UserRealm", - "text": "UserRealm" - } + "string | string[] | undefined" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.authentication_provider", + "id": "def-server.CheckPrivilegesPayload.elasticsearch", "type": "Object", "tags": [], - "label": "authentication_provider", + "label": "elasticsearch", "description": [ - "\nThe authentication provider that used to authenticate user." + "\nA set of the Elasticsearch cluster and index privileges." ], "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.AuthenticationProvider", - "text": "AuthenticationProvider" - } + "{ cluster: string[]; index: Record; } | undefined" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.authentication_type", - "type": "string", + "id": "def-server.CheckPrivilegesResponse.hasAllRequested", + "type": "boolean", "tags": [], - "label": "authentication_type", - "description": [ - "\nThe AuthenticationType used by ES to authenticate the user.\n" - ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "label": "hasAllRequested", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.elastic_cloud_user", - "type": "boolean", + "id": "def-server.CheckPrivilegesResponse.username", + "type": "string", "tags": [], - "label": "elastic_cloud_user", - "description": [ - "\nIndicates whether user is authenticated via Elastic Cloud built-in SAML realm." - ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "label": "username", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.AuthenticatedUser.profile_uid", - "type": "string", + "id": "def-server.CheckPrivilegesResponse.privileges", + "type": "Object", "tags": [], - "label": "profile_uid", - "description": [ - "\nUser profile ID of this user." - ], + "label": "privileges", + "description": [], "signature": [ - "string | undefined" + "{ kibana: { resource?: string | undefined; privilege: string; authorized: boolean; }[]; elasticsearch: { cluster: { privilege: string; authorized: boolean; }[]; index: { [indexName: string]: { privilege: string; authorized: boolean; }[]; }; }; }" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false } @@ -2129,213 +3610,163 @@ }, { "parentPluginId": "security", - "id": "def-server.AuthenticationServiceStart", + "id": "def-server.CheckUserProfilesPrivileges", "type": "Interface", "tags": [], - "label": "AuthenticationServiceStart", + "label": "CheckUserProfilesPrivileges", "description": [ - "\nAuthentication services available on the security plugin's start contract." + "\nAn interface to check users profiles privileges in a specific context (only a single-space context is supported at\nthe moment)." ], - "path": "x-pack/plugins/security/server/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuthenticationServiceStart.apiKeys", - "type": "Object", + "id": "def-server.CheckUserProfilesPrivileges.atSpace", + "type": "Function", "tags": [], - "label": "apiKeys", + "label": "atSpace", "description": [], "signature": [ - "{ create: (request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ", createParams: ", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.CreateAPIKeyParams", - "text": "CreateAPIKeyParams" - }, - ") => Promise<", - "SecurityCreateApiKeyResponse", - " | null>; validate: (apiKeyPrams: ", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.ValidateAPIKeyParams", - "text": "ValidateAPIKeyParams" - }, - ") => Promise; areAPIKeysEnabled: () => Promise; areCrossClusterAPIKeysEnabled: () => Promise; invalidate: (request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ", params: ", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.InvalidateAPIKeysParams", - "text": "InvalidateAPIKeysParams" - }, - ") => Promise<", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.InvalidateAPIKeyResult", - "text": "InvalidateAPIKeyResult" - }, - " | null>; grantAsInternalUser: (request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ", createParams: Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>) => Promise<", - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.GrantAPIKeyResult", - "text": "GrantAPIKeyResult" - }, - " | null>; invalidateAsInternalUser: (params: ", + "(spaceId: string, privileges: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.InvalidateAPIKeysParams", - "text": "InvalidateAPIKeysParams" + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" }, ") => Promise<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.InvalidateAPIKeyResult", - "text": "InvalidateAPIKeyResult" - }, - " | null>; }" - ], - "path": "x-pack/plugins/security/server/authentication/authentication_service.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.AuthenticationServiceStart.getCurrentUser", - "type": "Function", - "tags": [], - "label": "getCurrentUser", - "description": [], - "signature": [ - "(request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ") => ", - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.AuthenticatedUser", - "text": "AuthenticatedUser" + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesResponse", + "text": "CheckUserProfilesPrivilegesResponse" }, - " | null" + ">" ], - "path": "x-pack/plugins/security/server/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.AuthenticationServiceStart.getCurrentUser.$1", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$1", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$2", "type": "Object", "tags": [], - "label": "request", + "label": "privileges", "description": [], "signature": [ { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" + } ], - "path": "x-pack/plugins/security/server/authentication/authentication_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckUserProfilesPrivilegesPayload", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivilegesPayload", + "description": [ + "\nPrivileges that can be checked for the users profiles (only Kibana specific privileges are supported at the moment)." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckUserProfilesPrivilegesPayload.kibana", + "type": "Array", + "tags": [], + "label": "kibana", + "description": [ + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "security", - "id": "def-server.CheckPrivilegesPayload", + "id": "def-server.CheckUserProfilesPrivilegesResponse", "type": "Interface", "tags": [], - "label": "CheckPrivilegesPayload", + "label": "CheckUserProfilesPrivilegesResponse", "description": [ - "\nPrivileges that can be checked for the Kibana users." + "\nResponse of the check privileges operation for the users profiles." ], - "path": "x-pack/plugins/security/server/authorization/types.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.CheckPrivilegesPayload.kibana", - "type": "CompoundType", + "id": "def-server.CheckUserProfilesPrivilegesResponse.hasPrivilegeUids", + "type": "Array", "tags": [], - "label": "kibana", + "label": "hasPrivilegeUids", "description": [ - "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." + "\nThe subset of the requested profile IDs of the users that have all the requested privileges." ], "signature": [ - "string | string[] | undefined" + "string[]" ], - "path": "x-pack/plugins/security/server/authorization/types.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.CheckPrivilegesPayload.elasticsearch", + "id": "def-server.CheckUserProfilesPrivilegesResponse.errors", "type": "Object", "tags": [], - "label": "elasticsearch", + "label": "errors", "description": [ - "\nA set of the Elasticsearch cluster and index privileges." + "\nAn errors object that may be returned from ES that contains a `count` of UIDs that have errors in the `details` property.\n\nEach entry in `details` will contain an error `type`, e.g 'resource_not_found_exception', and a `reason` message, e.g. 'profile document not found'" ], "signature": [ - "{ cluster: string[]; index: Record; } | undefined" + "{ count: number; details: Record; } | undefined" ], - "path": "x-pack/plugins/security/server/authorization/types.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false } @@ -2349,7 +3780,7 @@ "tags": [], "label": "GrantAPIKeyResult", "description": [], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2362,7 +3793,7 @@ "description": [ "\nUnique id for this API key" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2375,7 +3806,7 @@ "description": [ "\nName for this API key" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2388,7 +3819,7 @@ "description": [ "\nGenerated API key" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -2404,7 +3835,7 @@ "description": [ "\nThe return value when invalidating an API key in Elasticsearch." ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2420,7 +3851,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2436,7 +3867,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2449,23 +3880,308 @@ "description": [ "\nThe number of errors that were encountered when invalidating the API keys." ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult.error_details", + "type": "Array", + "tags": [], + "label": "error_details", + "description": [ + "\nDetails about these errors. This field is not present in the response when error_count is 0." + ], + "signature": [ + "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeysParams", + "type": "Interface", + "tags": [], + "label": "InvalidateAPIKeysParams", + "description": [ + "\nRepresents the params for invalidating multiple API keys" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeysParams.ids", + "type": "Array", + "tags": [], + "label": "ids", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsRolesByFeatureIdRequest", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest.context", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-deprecations-server", + "scope": "common", + "docId": "kibKbnCoreDeprecationsServerPluginApi", + "section": "def-common.GetDeprecationsContext", + "text": "GetDeprecationsContext" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest.featureId", + "type": "string", + "tags": [], + "label": "featureId", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsRolesByFeatureIdResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse.roles", + "type": "Array", + "tags": [], + "label": "roles", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.Role", + "text": "Role" + }, + "[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult.error_details", - "type": "Array", + "id": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse.errors", + "type": "Array", + "tags": [], + "label": "errors", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-deprecations-common", + "scope": "common", + "docId": "kibKbnCoreDeprecationsCommonPluginApi", + "section": "def-common.DeprecationsDetails", + "text": "DeprecationsDetails" + }, + "[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsService", + "type": "Interface", + "tags": [], + "label": "PrivilegeDeprecationsService", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsService.getKibanaRolesByFeatureId", + "type": "Function", + "tags": [], + "label": "getKibanaRolesByFeatureId", + "description": [], + "signature": [ + "(args: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "text": "PrivilegeDeprecationsRolesByFeatureIdRequest" + }, + ") => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdResponse", + "text": "PrivilegeDeprecationsRolesByFeatureIdResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.PrivilegeDeprecationsService.getKibanaRolesByFeatureId.$1", + "type": "Object", + "tags": [], + "label": "args", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.PrivilegeDeprecationsRolesByFeatureIdRequest", + "text": "PrivilegeDeprecationsRolesByFeatureIdRequest" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.SavedObjectActions", + "type": "Interface", + "tags": [], + "label": "SavedObjectActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.SavedObjectActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(type: string, operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.SavedObjectActions.get.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.SavedObjectActions.get.$2", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.SpaceActions", + "type": "Interface", + "tags": [], + "label": "SpaceActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.SpaceActions.manage", + "type": "string", "tags": [], - "label": "error_details", - "description": [ - "\nDetails about these errors. This field is not present in the response when error_count is 0." - ], - "signature": [ - "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" - ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "label": "manage", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts", "deprecated": false, "trackAdoption": false } @@ -2474,30 +4190,76 @@ }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeysParams", + "id": "def-server.UIActions", "type": "Interface", "tags": [], - "label": "InvalidateAPIKeysParams", - "description": [ - "\nRepresents the params for invalidating multiple API keys" - ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "label": "UIActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeysParams.ids", - "type": "Array", + "id": "def-server.UIActions.get", + "type": "Function", "tags": [], - "label": "ids", + "label": "get", "description": [], "signature": [ - "string[]" + "(featureId: keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + }, + ", ...uiCapabilityParts: string[]) => string" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.UIActions.get.$1", + "type": "CompoundType", + "tags": [], + "label": "featureId", + "description": [], + "signature": [ + "keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.UIActions.get.$2", + "type": "Array", + "tags": [], + "label": "uiCapabilityParts", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -2511,7 +4273,7 @@ "description": [ "\nParameters for the bulk get API." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2527,7 +4289,7 @@ "signature": [ "Set" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2543,7 +4305,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false } @@ -2559,7 +4321,7 @@ "description": [ "\nParameters for the get user profile for the current user API." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2582,7 +4344,7 @@ }, "" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2598,7 +4360,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false } @@ -2614,7 +4376,7 @@ "description": [ "\nThe set of privileges that users associated with the suggested user profile should have for a specified space id." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2627,7 +4389,7 @@ "description": [ "\nThe id of the Kibana Space." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2643,7 +4405,7 @@ "signature": [ "{ kibana: string[]; }" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false } @@ -2659,7 +4421,7 @@ "description": [ "\nA set of methods to work with Kibana user profiles." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2675,39 +4437,39 @@ "signature": [ "(params: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileGetCurrentParams", "text": "UserProfileGetCurrentParams" }, ") => Promise<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileWithSecurity", "text": "UserProfileWithSecurity" }, " | null>" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2722,14 +4484,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileGetCurrentParams", "text": "UserProfileGetCurrentParams" } ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2749,31 +4511,31 @@ "signature": [ "(params: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileBulkGetParams", "text": "UserProfileBulkGetParams" }, ") => Promise<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfile", "text": "UserProfile" }, "[]>" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2788,14 +4550,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileBulkGetParams", "text": "UserProfileBulkGetParams" } ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2815,31 +4577,31 @@ "signature": [ "(params: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileSuggestParams", "text": "UserProfileSuggestParams" }, ") => Promise<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfile", "text": "UserProfile" }, "[]>" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2854,14 +4616,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileSuggestParams", "text": "UserProfileSuggestParams" } ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2881,7 +4643,7 @@ "description": [ "\nParameters for the suggest API." ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2897,7 +4659,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2913,7 +4675,7 @@ "signature": [ "{ uids: string[]; } | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2929,7 +4691,7 @@ "signature": [ "number | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", "deprecated": false, "trackAdoption": false }, @@ -2945,30 +4707,281 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.UserProfileSuggestParams.requiredPrivileges", + "type": "Object", + "tags": [], + "label": "requiredPrivileges", + "description": [ + "\nThe set of the privileges that users associated with the suggested user profile should have in the specified space.\nIf not specified, privileges check isn't performed and all matched profiles are returned irrespective to the\nprivileges of the associated users." + ], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.UserProfileRequiredPrivileges", + "text": "UserProfileRequiredPrivileges" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.ValidateAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "ValidateAPIKeyParams", + "description": [ + "\nRepresents the parameters for validating API Key credentials." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.ValidateAPIKeyParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique id for this API key" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.ValidateAPIKeyParams.api_key", + "type": "string", + "tags": [], + "label": "api_key", + "description": [ + "\nGenerated API Key (secret)" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "security", + "id": "def-server.CasesSupportedOperations", + "type": "Type", + "tags": [], + "label": "CasesSupportedOperations", + "description": [], + "signature": [ + "\"getTags\" | \"pushCase\" | \"createCase\" | \"createComment\" | \"getCase\" | \"getComment\" | \"getReporters\" | \"getUserActions\" | \"findConfigurations\" | \"updateCase\" | \"updateComment\" | \"deleteCase\" | \"deleteComment\" | \"createConfiguration\" | \"updateConfiguration\"" + ], + "path": "x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesDynamically", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesDynamically", + "description": [], + "signature": [ + "(privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesDynamically.$1", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesDynamically.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesDynamicallyWithRequest", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesDynamicallyWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesDynamically", + "text": "CheckPrivilegesDynamically" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesDynamicallyWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts", "deprecated": false, "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CheckPrivilegesWithRequest", + "type": "Type", + "tags": [], + "label": "CheckPrivilegesWithRequest", + "description": [], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivileges", + "text": "CheckPrivileges" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ { "parentPluginId": "security", - "id": "def-server.UserProfileSuggestParams.requiredPrivileges", + "id": "def-server.CheckPrivilegesWithRequest.$1", "type": "Object", "tags": [], - "label": "requiredPrivileges", - "description": [ - "\nThe set of the privileges that users associated with the suggested user profile should have in the specified space.\nIf not specified, privileges check isn't performed and all matched profiles are returned irrespective to the\nprivileges of the associated users." - ], + "label": "request", + "description": [], "signature": [ { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.UserProfileRequiredPrivileges", - "text": "UserProfileRequiredPrivileges" + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" }, - " | undefined" + "" ], - "path": "x-pack/plugins/security/server/user_profile/user_profile_service.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false } @@ -2977,74 +4990,110 @@ }, { "parentPluginId": "security", - "id": "def-server.ValidateAPIKeyParams", - "type": "Interface", + "id": "def-server.CheckSavedObjectsPrivileges", + "type": "Type", "tags": [], - "label": "ValidateAPIKeyParams", - "description": [ - "\nRepresents the parameters for validating API Key credentials." + "label": "CheckSavedObjectsPrivileges", + "description": [], + "signature": [ + "(actions: string | string[], namespaceOrNamespaces?: string | (string | undefined)[] | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", "deprecated": false, "trackAdoption": false, + "returnComment": [], "children": [ { "parentPluginId": "security", - "id": "def-server.ValidateAPIKeyParams.id", - "type": "string", + "id": "def-server.CheckSavedObjectsPrivileges.$1", + "type": "CompoundType", "tags": [], - "label": "id", - "description": [ - "\nUnique id for this API key" + "label": "actions", + "description": [], + "signature": [ + "string | string[]" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.ValidateAPIKeyParams.api_key", - "type": "string", + "id": "def-server.CheckSavedObjectsPrivileges.$2", + "type": "CompoundType", "tags": [], - "label": "api_key", - "description": [ - "\nGenerated API Key (secret)" + "label": "namespaceOrNamespaces", + "description": [], + "signature": [ + "string | (string | undefined)[] | undefined" ], - "path": "x-pack/plugins/security/server/authentication/api_keys/api_keys.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", "deprecated": false, "trackAdoption": false } ], "initialIsOpen": false - } - ], - "enums": [], - "misc": [ - { - "parentPluginId": "security", - "id": "def-server.AuthorizationServiceSetup", - "type": "Type", - "tags": [], - "label": "AuthorizationServiceSetup", - "description": [], - "path": "x-pack/plugins/security/server/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false }, { "parentPluginId": "security", - "id": "def-server.CasesSupportedOperations", + "id": "def-server.CheckSavedObjectsPrivilegesWithRequest", "type": "Type", "tags": [], - "label": "CasesSupportedOperations", + "label": "CheckSavedObjectsPrivilegesWithRequest", "description": [], "signature": [ - "\"getTags\" | \"pushCase\" | \"createCase\" | \"createComment\" | \"createConfiguration\" | \"getCase\" | \"getComment\" | \"getReporters\" | \"getUserActions\" | \"findConfigurations\" | \"updateCase\" | \"updateComment\" | \"updateConfiguration\" | \"deleteCase\" | \"deleteComment\"" + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckSavedObjectsPrivileges", + "text": "CheckSavedObjectsPrivileges" + } ], - "path": "x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", "deprecated": false, "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CheckSavedObjectsPrivilegesWithRequest.$1", + "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/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], "initialIsOpen": false }, { @@ -3053,13 +5102,11 @@ "type": "Type", "tags": [], "label": "CreateAPIKeyParams", - "description": [ - "\nRequest body of Kibana Create API key endpoint." - ], + "description": [], "signature": [ "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { type: \"cross_cluster\"; name: string; access: Readonly<{ search?: Readonly<{} & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }>" ], - "path": "x-pack/plugins/security/server/routes/api_keys/create.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -3076,7 +5123,7 @@ "signature": [ "SecurityCreateApiKeyResponse" ], - "path": "x-pack/plugins/security/server/routes/api_keys/create.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -3091,7 +5138,7 @@ "signature": [ "{ readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly type: \"cross_cluster\"; readonly name: string; readonly access: Readonly<{ search?: Readonly<{} & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }" ], - "path": "x-pack/plugins/security/server/routes/api_keys/create.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -3106,7 +5153,7 @@ "signature": [ "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly role_descriptors: Record>; }" ], - "path": "x-pack/plugins/security/server/routes/api_keys/create.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -3121,7 +5168,37 @@ "signature": [ "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }" ], - "path": "x-pack/plugins/security/server/routes/api_keys/create.ts", + "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType", + "type": "Type", + "tags": [], + "label": "ElasticsearchPrivilegesType", + "description": [], + "signature": [ + "{ readonly cluster?: string[] | undefined; readonly indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; readonly remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"grant\" | \"except\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; readonly run_as?: string[] | undefined; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.KibanaPrivilegesType", + "type": "Type", + "tags": [], + "label": "KibanaPrivilegesType", + "description": [], + "signature": [ + "Readonly<{ base?: string[] | undefined; feature?: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -3154,6 +5231,23 @@ "description": [ "\nDescribes public Security plugin contract returned at the `setup` stage." ], + "signature": [ + { + "pluginId": "security", + "scope": "server", + "docId": "kibSecurityPluginApi", + "section": "def-server.SecurityPluginSetup", + "text": "SecurityPluginSetup" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.SecurityPluginSetup", + "text": "SecurityPluginSetup" + } + ], "path": "x-pack/plugins/security/server/plugin.ts", "deprecated": false, "trackAdoption": false, @@ -3178,9 +5272,9 @@ }, ") => ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, @@ -3259,9 +5353,9 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.AuthorizationServiceSetup", "text": "AuthorizationServiceSetup" } @@ -3307,72 +5401,6 @@ "path": "x-pack/plugins/enterprise_search/server/lib/check_access.ts" } ] - }, - { - "parentPluginId": "security", - "id": "def-server.SecurityPluginSetup.license", - "type": "Object", - "tags": [], - "label": "license", - "description": [ - "\nExposes information about the available security features under the current license." - ], - "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.SecurityLicense", - "text": "SecurityLicense" - } - ], - "path": "x-pack/plugins/security/server/plugin.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.SecurityPluginSetup.audit", - "type": "Object", - "tags": [], - "label": "audit", - "description": [ - "\nExposes services for audit logging." - ], - "signature": [ - { - "pluginId": "security", - "scope": "server", - "docId": "kibSecurityPluginApi", - "section": "def-server.AuditServiceSetup", - "text": "AuditServiceSetup" - } - ], - "path": "x-pack/plugins/security/server/plugin.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "security", - "id": "def-server.SecurityPluginSetup.privilegeDeprecationsService", - "type": "Object", - "tags": [], - "label": "privilegeDeprecationsService", - "description": [ - "\nExposes services to access kibana roles per feature id with the GetDeprecationsContext" - ], - "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.PrivilegeDeprecationsService", - "text": "PrivilegeDeprecationsService" - } - ], - "path": "x-pack/plugins/security/server/plugin.ts", - "deprecated": false, - "trackAdoption": false } ], "lifecycle": "setup", @@ -3387,7 +5415,7 @@ "description": [ "\nDescribes public Security plugin contract returned at the `start` stage." ], - "path": "x-pack/plugins/security/server/plugin.ts", + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -3402,14 +5430,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.AuthenticationServiceStart", "text": "AuthenticationServiceStart" } ], - "path": "x-pack/plugins/security/server/plugin.ts", + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", "deprecated": false, "trackAdoption": false }, @@ -3424,14 +5452,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.AuthorizationServiceSetup", "text": "AuthorizationServiceSetup" } ], - "path": "x-pack/plugins/security/server/plugin.ts", + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", "deprecated": false, "trackAdoption": false }, @@ -3446,14 +5474,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-server", "scope": "server", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", "section": "def-server.UserProfileServiceStart", "text": "UserProfileServiceStart" } ], - "path": "x-pack/plugins/security/server/plugin.ts", + "path": "x-pack/packages/security/plugin_types_server/src/plugin.ts", "deprecated": false, "trackAdoption": false } @@ -3467,47 +5495,100 @@ "functions": [ { "parentPluginId": "security", - "id": "def-common.getUserDisplayName", + "id": "def-common.getUserDisplayName", + "type": "Function", + "tags": [], + "label": "getUserDisplayName", + "description": [ + "\nDetermines the display name for the provided user information." + ], + "signature": [ + "(params: ", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.GetUserDisplayNameParams", + "text": "GetUserDisplayNameParams" + }, + ") => string" + ], + "path": "x-pack/plugins/security/common/model/user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.getUserDisplayName.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "Set of available user's name-related fields." + ], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.GetUserDisplayNameParams", + "text": "GetUserDisplayNameParams" + } + ], + "path": "x-pack/plugins/security/common/model/user.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.isRoleReserved", "type": "Function", "tags": [], - "label": "getUserDisplayName", + "label": "isRoleReserved", "description": [ - "\nDetermines the display name for the provided user information." + "\nReturns whether given role is reserved or not.\n" ], "signature": [ - "(params: ", + "(role: Partial<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.GetUserDisplayNameParams", - "text": "GetUserDisplayNameParams" + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.Role", + "text": "Role" }, - ") => string" + ">) => boolean" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/plugins/security/common/model/role.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-common.getUserDisplayName.$1", + "id": "def-common.isRoleReserved.$1", "type": "Object", "tags": [], - "label": "params", + "label": "role", "description": [ - "Set of available user's name-related fields." + "Role as returned by roles API" ], "signature": [ + "Partial<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.GetUserDisplayNameParams", - "text": "GetUserDisplayNameParams" - } + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.Role", + "text": "Role" + }, + ">" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/plugins/security/common/model/role.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -3529,22 +5610,22 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticatedUser", "text": "AuthenticatedUser" }, " extends ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.User", "text": "User" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -3559,14 +5640,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserRealm", "text": "UserRealm" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -3581,14 +5662,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserRealm", "text": "UserRealm" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -3603,14 +5684,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.AuthenticationProvider", "text": "AuthenticationProvider" } ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -3623,7 +5704,7 @@ "description": [ "\nThe AuthenticationType used by ES to authenticate the user.\n" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -3636,7 +5717,7 @@ "description": [ "\nIndicates whether user is authenticated via Elastic Cloud built-in SAML realm." ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -3652,7 +5733,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false } @@ -3668,7 +5749,7 @@ "description": [ "\nType and name tuple to identify provider used to authenticate user." ], - "path": "x-pack/plugins/security/common/model/authentication_provider.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -3681,7 +5762,7 @@ "description": [ "\nType of the Kibana authentication provider." ], - "path": "x-pack/plugins/security/common/model/authentication_provider.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", "deprecated": false, "trackAdoption": false }, @@ -3694,7 +5775,108 @@ "description": [ "\nName of the Kibana authentication provider (arbitrary string)." ], - "path": "x-pack/plugins/security/common/model/authentication_provider.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.BuiltinESPrivileges", + "type": "Interface", + "tags": [], + "label": "BuiltinESPrivileges", + "description": [], + "path": "x-pack/plugins/security/common/model/builtin_es_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.BuiltinESPrivileges.cluster", + "type": "Array", + "tags": [], + "label": "cluster", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/security/common/model/builtin_es_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.BuiltinESPrivileges.index", + "type": "Array", + "tags": [], + "label": "index", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/security/common/model/builtin_es_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.EditUser", + "type": "Interface", + "tags": [], + "label": "EditUser", + "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.EditUser", + "text": "EditUser" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.User", + "text": "User" + } + ], + "path": "x-pack/plugins/security/common/model/user.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.EditUser.password", + "type": "string", + "tags": [], + "label": "password", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/common/model/user.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.EditUser.confirmPassword", + "type": "string", + "tags": [], + "label": "confirmPassword", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security/common/model/user.ts", "deprecated": false, "trackAdoption": false } @@ -3708,7 +5890,7 @@ "tags": [], "label": "FeaturesPrivileges", "description": [], - "path": "x-pack/plugins/security/common/model/features_privileges.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -3722,7 +5904,7 @@ "signature": [ "[featureId: string]: string[]" ], - "path": "x-pack/plugins/security/common/model/features_privileges.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts", "deprecated": false, "trackAdoption": false } @@ -3809,17 +5991,17 @@ }, " extends ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileWithSecurity", "text": "UserProfileWithSecurity" }, " Promise<", - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.PrivilegeDeprecationsRolesByFeatureIdResponse", - "text": "PrivilegeDeprecationsRolesByFeatureIdResponse" - }, - ">" + "{ [x: string]: string[]; }" ], - "path": "x-pack/plugins/security/common/model/deprecations.ts", + "path": "x-pack/plugins/security/common/model/raw_kibana_privileges.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "security", - "id": "def-common.PrivilegeDeprecationsService.getKibanaRolesByFeatureId.$1", - "type": "Object", - "tags": [], - "label": "args", - "description": [], - "signature": [ - { - "pluginId": "security", - "scope": "common", - "docId": "kibSecurityPluginApi", - "section": "def-common.PrivilegeDeprecationsRolesByFeatureIdRequest", - "text": "PrivilegeDeprecationsRolesByFeatureIdRequest" - } - ], - "path": "x-pack/plugins/security/common/model/deprecations.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RawKibanaPrivileges.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "signature": [ + "RawKibanaFeaturePrivileges" + ], + "path": "x-pack/plugins/security/common/model/raw_kibana_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RawKibanaPrivileges.space", + "type": "Object", + "tags": [], + "label": "space", + "description": [], + "signature": [ + "{ [x: string]: string[]; }" + ], + "path": "x-pack/plugins/security/common/model/raw_kibana_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RawKibanaPrivileges.reserved", + "type": "Object", + "tags": [], + "label": "reserved", + "description": [], + "signature": [ + "{ [x: string]: string[]; }" + ], + "path": "x-pack/plugins/security/common/model/raw_kibana_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.RestApiKey", + "type": "Interface", + "tags": [], + "label": "RestApiKey", + "description": [ + "\nInterface representing a REST API key the way it is returned by Elasticsearch GET endpoint.\n\nTODO: Remove this type when `@elastic/elasticsearch` has been updated." + ], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.RestApiKey", + "text": "RestApiKey" + }, + " extends BaseApiKey" + ], + "path": "x-pack/plugins/security/common/model/api_key.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.RestApiKey.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\"" ], - "returnComment": [] + "path": "x-pack/plugins/security/common/model/api_key.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -4039,7 +6243,7 @@ "tags": [], "label": "Role", "description": [], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4050,7 +6254,7 @@ "tags": [], "label": "name", "description": [], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4064,17 +6268,23 @@ "signature": [ "{ cluster: string[]; indices: ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.RoleIndexPrivilege", "text": "RoleIndexPrivilege" }, "[]; remote_indices?: ", - "RoleRemoteIndexPrivilege", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleRemoteIndexPrivilege", + "text": "RoleRemoteIndexPrivilege" + }, "[] | undefined; run_as: string[]; }" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4087,15 +6297,15 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.RoleKibanaPrivilege", "text": "RoleKibanaPrivilege" }, "[]" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4109,7 +6319,7 @@ "signature": [ "{ [anyKey: string]: any; } | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4123,7 +6333,7 @@ "signature": [ "{ [anyKey: string]: any; } | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4137,7 +6347,7 @@ "signature": [ "string[] | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4151,7 +6361,7 @@ "signature": [ "string[] | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false } @@ -4165,7 +6375,7 @@ "tags": [], "label": "RoleIndexPrivilege", "description": [], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4179,7 +6389,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4193,7 +6403,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4207,7 +6417,7 @@ "signature": [ "{ grant?: string[] | undefined; except?: string[] | undefined; } | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4221,7 +6431,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false } @@ -4235,7 +6445,7 @@ "tags": [], "label": "RoleKibanaPrivilege", "description": [], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4249,7 +6459,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4263,7 +6473,7 @@ "signature": [ "string[]" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4276,14 +6486,14 @@ "description": [], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.FeaturesPrivileges", "text": "FeaturesPrivileges" } ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false }, @@ -4297,7 +6507,151 @@ "signature": [ "string[] | undefined" ], - "path": "x-pack/plugins/security/common/model/role.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping", + "type": "Interface", + "tags": [], + "label": "RoleMapping", + "description": [], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.roles", + "type": "Array", + "tags": [], + "label": "roles", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.role_templates", + "type": "Array", + "tags": [], + "label": "role_templates", + "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.RoleTemplate", + "text": "RoleTemplate" + }, + "[] | undefined" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.rules", + "type": "CompoundType", + "tags": [], + "label": "rules", + "description": [], + "signature": [ + "{} | RoleMappingRule" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleMapping.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [x: string]: any; }" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleRemoteIndexPrivilege", + "type": "Interface", + "tags": [], + "label": "RoleRemoteIndexPrivilege", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleRemoteIndexPrivilege", + "text": "RoleRemoteIndexPrivilege" + }, + " extends ", + { + "pluginId": "@kbn/security-plugin-types-common", + "scope": "common", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", + "section": "def-common.RoleIndexPrivilege", + "text": "RoleIndexPrivilege" + } + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.RoleRemoteIndexPrivilege.clusters", + "type": "Array", + "tags": [], + "label": "clusters", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_common/src/authorization/role.ts", "deprecated": false, "trackAdoption": false } @@ -4311,7 +6665,7 @@ "tags": [], "label": "SecurityLicense", "description": [], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4325,7 +6679,7 @@ "signature": [ "() => boolean" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -4341,7 +6695,7 @@ "signature": [ "() => boolean" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -4357,14 +6711,14 @@ "signature": [ "() => ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.SecurityLicenseFeatures", "text": "SecurityLicenseFeatures" } ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -4380,7 +6734,7 @@ "signature": [ "(licenseType: \"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\") => boolean | undefined" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4394,7 +6748,7 @@ "signature": [ "\"basic\" | \"standard\" | \"gold\" | \"platinum\" | \"enterprise\" | \"trial\"" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -4413,15 +6767,15 @@ "Observable", "<", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.SecurityLicenseFeatures", "text": "SecurityLicenseFeatures" }, ">" ], - "path": "x-pack/plugins/security/common/licensing/license_service.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license.ts", "deprecated": false, "trackAdoption": false } @@ -4437,7 +6791,7 @@ "description": [ "\nDescribes Security plugin features that depend on license." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4450,7 +6804,7 @@ "description": [ "\nIndicates whether we show login page or skip it." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4463,7 +6817,7 @@ "description": [ "\nIndicates whether we allow login or disable it on the login page." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4476,7 +6830,7 @@ "description": [ "\nIndicates whether we show security links throughout the kibana app." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4489,7 +6843,7 @@ "description": [ "\nIndicates whether we show the Role Mappings UI." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4502,7 +6856,7 @@ "description": [ "\nIndicates whether we allow users to access agreement UI and acknowledge it." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4515,7 +6869,7 @@ "description": [ "\nIndicates whether we allow logging of audit events." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4528,7 +6882,7 @@ "description": [ "\nIndicates whether we allow users to define document level security in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4541,7 +6895,7 @@ "description": [ "\nIndicates whether we allow users to define field level security in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4554,7 +6908,7 @@ "description": [ "\nIndicates whether we allow users to define remote index privileges in roles." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4567,7 +6921,7 @@ "description": [ "\nIndicates whether we allow Role-based access control (RBAC)." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4580,7 +6934,7 @@ "description": [ "\nIndicates whether we allow sub-feature privileges." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4593,7 +6947,7 @@ "description": [ "\nIndicates whether we allow user profile collaboration features (suggest and privileges checks APIs)." ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", "deprecated": false, "trackAdoption": false }, @@ -4608,15 +6962,57 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.LoginLayout", "text": "LoginLayout" }, " | undefined" ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.StoredRoleTemplate", + "type": "Interface", + "tags": [], + "label": "StoredRoleTemplate", + "description": [], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-common.StoredRoleTemplate.template", + "type": "Object", + "tags": [], + "label": "template", + "description": [], + "signature": [ + "{ id: string; }" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-common.StoredRoleTemplate.format", + "type": "CompoundType", + "tags": [], + "label": "format", + "description": [], + "signature": [ + "RoleTemplateFormat | undefined" + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", "deprecated": false, "trackAdoption": false } @@ -4632,7 +7028,7 @@ "description": [ "\nA set of fields describing Kibana user." ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4643,7 +7039,7 @@ "tags": [], "label": "username", "description": [], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false }, @@ -4657,7 +7053,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false }, @@ -4671,7 +7067,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false }, @@ -4685,7 +7081,7 @@ "signature": [ "readonly string[]" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false }, @@ -4696,7 +7092,7 @@ "tags": [], "label": "enabled", "description": [], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false }, @@ -4710,7 +7106,7 @@ "signature": [ "{ _reserved: boolean; _deprecated?: boolean | undefined; _deprecated_reason?: string | undefined; } | undefined" ], - "path": "x-pack/plugins/security/common/model/user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/user.ts", "deprecated": false, "trackAdoption": false } @@ -4728,15 +7124,15 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfile", "text": "UserProfile" }, "" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4749,7 +7145,7 @@ "description": [ "\nUnique ID for of the user profile." ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4762,7 +7158,7 @@ "description": [ "\nIndicates whether user profile is enabled or not." ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4777,14 +7173,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileUserInfo", "text": "UserProfileUserInfo" } ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4800,7 +7196,7 @@ "signature": [ "{ [P in keyof D]?: D[P] | undefined; }" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false } @@ -4816,7 +7212,7 @@ "description": [ "\nBasic user information returned in user profile." ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4829,7 +7225,7 @@ "description": [ "\nUsername of the user." ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4845,7 +7241,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4861,7 +7257,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false } @@ -4879,22 +7275,22 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileUserInfoWithSecurity", "text": "UserProfileUserInfoWithSecurity" }, " extends ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileUserInfo", "text": "UserProfileUserInfo" } ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4910,7 +7306,7 @@ "signature": [ "readonly string[]" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4923,7 +7319,7 @@ "description": [ "\nName of the Elasticsearch security realm that was used to authenticate user." ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -4939,7 +7335,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false } @@ -4957,23 +7353,23 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileWithSecurity", "text": "UserProfileWithSecurity" }, " extends ", { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfile", "text": "UserProfile" }, "" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4988,14 +7384,14 @@ ], "signature": [ { - "pluginId": "security", + "pluginId": "@kbn/security-plugin-types-common", "scope": "common", - "docId": "kibSecurityPluginApi", + "docId": "kibKbnSecurityPluginTypesCommonPluginApi", "section": "def-common.UserProfileUserInfoWithSecurity", "text": "UserProfileUserInfoWithSecurity" } ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false }, @@ -5011,7 +7407,7 @@ "signature": [ "L" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false } @@ -5027,7 +7423,7 @@ "description": [ "\nAn Elasticsearch realm that was used to resolve and authenticate the user." ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -5040,7 +7436,7 @@ "description": [ "\nArbitrary name of the security realm." ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false }, @@ -5053,7 +7449,7 @@ "description": [ "\nType of the security realm (file, native, saml etc.)." ], - "path": "x-pack/plugins/security/common/model/authenticated_user.ts", + "path": "x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts", "deprecated": false, "trackAdoption": false } @@ -5073,7 +7469,13 @@ "\nInterface representing an API key the way it is returned by Elasticsearch GET endpoint." ], "signature": [ - "RestApiKey", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.RestApiKey", + "text": "RestApiKey" + }, " | ", "CrossClusterApiKey" ], @@ -5094,7 +7496,44 @@ "signature": [ "\"form\" | \"error-es-unavailable\" | \"error-xpack-unavailable\"" ], - "path": "x-pack/plugins/security/common/licensing/license_features.ts", + "path": "x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-common.RoleTemplate", + "type": "Type", + "tags": [], + "label": "RoleTemplate", + "description": [], + "signature": [ + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.InlineRoleTemplate", + "text": "InlineRoleTemplate" + }, + " | ", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.StoredRoleTemplate", + "text": "StoredRoleTemplate" + }, + " | ", + { + "pluginId": "security", + "scope": "common", + "docId": "kibSecurityPluginApi", + "section": "def-common.InvalidRoleTemplate", + "text": "InvalidRoleTemplate" + } + ], + "path": "x-pack/plugins/security/common/model/role_mapping.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5111,7 +7550,7 @@ "signature": [ "{ [x: string]: unknown; }" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5128,7 +7567,7 @@ "signature": [ "{ [x: string]: string; }" ], - "path": "x-pack/plugins/security/common/model/user_profile.ts", + "path": "x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 7aa850ed34e1e..514ec8f2db66e 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 270 | 0 | 87 | 3 | +| 401 | 0 | 196 | 2 | ## Client diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index 1fa3537631f7b..a893eeffd3155 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -114,7 +114,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, @@ -507,7 +507,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -587,7 +587,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1760,6 +1760,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "securitySolution", + "id": "def-public.TimelineModel.isDataProviderVisible", + "type": "boolean", + "tags": [], + "label": "isDataProviderVisible", + "description": [], + "path": "x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "securitySolution", "id": "def-public.TimelineModel.changed", @@ -1820,7 +1831,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -1972,9 +1983,7 @@ "label": "setComponents", "description": [], "signature": [ - "(components: Partial>>) => void" + "(components: Partial<{ GetStarted: React.ComponentType<{}>; DashboardsLandingCallout: React.ComponentType<{}>; }>) => void" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -1989,7 +1998,7 @@ "label": "components", "description": [], "signature": [ - "{ getStarted?: React.ComponentType<{}> | undefined; dashboardsLandingCallout?: React.ComponentType<{}> | undefined; }" + "{ GetStarted?: React.ComponentType<{}> | undefined; DashboardsLandingCallout?: React.ComponentType<{}> | undefined; }" ], "path": "x-pack/plugins/security_solution/public/contract_components.ts", "deprecated": false, @@ -2803,6 +2812,40 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "securitySolution", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.getRiskScoreDataClient", + "type": "Function", + "tags": [], + "label": "getRiskScoreDataClient", + "description": [], + "signature": [ + "() => ", + "RiskScoreDataClient" + ], + "path": "x-pack/plugins/security_solution/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "securitySolution", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.getAssetCriticalityDataClient", + "type": "Function", + "tags": [], + "label": "getAssetCriticalityDataClient", + "description": [], + "signature": [ + "() => ", + "AssetCriticalityDataClient" + ], + "path": "x-pack/plugins/security_solution/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -2893,7 +2936,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3039,7 +3082,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3088,7 +3131,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 4ec0fe733a33d..48d50a9e307d4 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 175 | 0 | 106 | 35 | +| 178 | 0 | 109 | 36 | ## Client diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 0781ac2e044e9..77e2f3b709bb1 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index 43fe1ae52fbf7..e5bb6205f39b2 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 4864050d01a9a..de5719b4297e9 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index bfd4d6de8152f..eb1b4c614a89f 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index 43a2ca98e6de1..964f8a62ef39c 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 92495133a4a3e..f476a884606a5 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-11-22 +date: 2023-11-30 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 499313b71291a..062290680fd80 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-11-22 +date: 2023-11-30 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 88b3210ba7d01..20789067030af 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-11-22 +date: 2023-11-30 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 34521541c04f6..bfe873fcbdd13 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-11-22 +date: 2023-11-30 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 f38bb1f53b863..031f7a13cad0f 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-11-22 +date: 2023-11-30 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 1a3e2035b126c..a440d1f197a11 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-11-22 +date: 2023-11-30 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 0ad6c93791e72..9954d2bc0d264 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 705105a434846..ea35a897980f4 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-11-22 +date: 2023-11-30 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 7d58bdd54854a..7ed6d4f7f12ed 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-11-22 +date: 2023-11-30 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 cb0a97e8a8ec4..0c02ad4a63660 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-11-22 +date: 2023-11-30 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 a78354ca805d6..82e7d78584342 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.devdocs.json b/api_docs/text_based_languages.devdocs.json index c44e1664a3fe1..bfbd1ef629c7f 100644 --- a/api_docs/text_based_languages.devdocs.json +++ b/api_docs/text_based_languages.devdocs.json @@ -69,7 +69,9 @@ "type": "CompoundType", "tags": [], "label": "query", - "description": [], + "description": [ + "The aggregate type query" + ], "signature": [ "{ sql: string; } | { esql: string; }" ], @@ -83,7 +85,9 @@ "type": "Function", "tags": [], "label": "onTextLangQueryChange", - "description": [], + "description": [ + "Callback running everytime the query changes" + ], "signature": [ "(query: ", { @@ -129,14 +133,47 @@ "type": "Function", "tags": [], "label": "onTextLangQuerySubmit", - "description": [], + "description": [ + "Callback running when the user submits the query" + ], "signature": [ - "() => void" + "(query?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined) => void" ], "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "textBasedLanguages", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQuerySubmit.$1", + "type": "CompoundType", + "tags": [], + "label": "query", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], "returnComment": [] }, { @@ -145,7 +182,9 @@ "type": "Function", "tags": [], "label": "expandCodeEditor", - "description": [], + "description": [ + "Can be used to expand/minimize the editor" + ], "signature": [ "(status: boolean) => void" ], @@ -177,7 +216,9 @@ "type": "boolean", "tags": [], "label": "isCodeEditorExpanded", - "description": [], + "description": [ + "If it is true, the editor initializes with height EDITOR_INITIAL_HEIGHT_EXPANDED" + ], "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", "deprecated": false, "trackAdoption": false @@ -188,7 +229,9 @@ "type": "CompoundType", "tags": [], "label": "detectTimestamp", - "description": [], + "description": [ + "If it is true, the editor displays the message @timestamp found\nThe text based queries are relying on adhoc dataviews which\ncan have an @timestamp timefield or nothing" + ], "signature": [ "boolean | undefined" ], @@ -202,7 +245,9 @@ "type": "Array", "tags": [], "label": "errors", - "description": [], + "description": [ + "Array of errors" + ], "signature": [ "Error[] | undefined" ], @@ -216,7 +261,9 @@ "type": "string", "tags": [], "label": "warning", - "description": [], + "description": [ + "Warning string as it comes from ES" + ], "signature": [ "string | undefined" ], @@ -230,7 +277,9 @@ "type": "CompoundType", "tags": [], "label": "isDisabled", - "description": [], + "description": [ + "Disables the editor" + ], "signature": [ "boolean | undefined" ], @@ -244,7 +293,9 @@ "type": "CompoundType", "tags": [], "label": "isDarkMode", - "description": [], + "description": [ + "Indicator if the editor is on dark mode" + ], "signature": [ "boolean | undefined" ], @@ -272,7 +323,9 @@ "type": "CompoundType", "tags": [], "label": "hideMinimizeButton", - "description": [], + "description": [ + "If true it hides the minimize button and the user can't return to the minimized version\nUseful when the application doesn't want to give this capability" + ], "signature": [ "boolean | undefined" ], @@ -286,7 +339,41 @@ "type": "CompoundType", "tags": [], "label": "hideRunQueryText", - "description": [], + "description": [ + "Hide the Run query information which appears on the footer" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "textBasedLanguages", + "id": "def-public.TextBasedLanguagesEditorProps.editorIsInline", + "type": "CompoundType", + "tags": [], + "label": "editorIsInline", + "description": [ + "This is used for applications (such as the inline editing flyout in dashboards)\nwhich want to add the editor without being part of the Unified search component\nIt renders a submit query button inside the editor" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "textBasedLanguages", + "id": "def-public.TextBasedLanguagesEditorProps.disableSubmitAction", + "type": "CompoundType", + "tags": [], + "label": "disableSubmitAction", + "description": [ + "Disables the submit query action" + ], "signature": [ "boolean | undefined" ], diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index c21e0821c4207..6e48843cfb435 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 20 | 0 | 20 | 0 | +| 23 | 0 | 9 | 0 | ## Client diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 155e8ffa8fa6a..de3d17bc5fbeb 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-11-22 +date: 2023-11-30 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 d74e4b714301a..62a75fefbeaa7 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-11-22 +date: 2023-11-30 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 a1979a9c2a0b0..5cb3f87fd5360 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index fa33ca9250fd1..cb32d2a992b6b 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.devdocs.json b/api_docs/ui_actions.devdocs.json index 514e137c5d6e7..bf583998fc74e 100644 --- a/api_docs/ui_actions.devdocs.json +++ b/api_docs/ui_actions.devdocs.json @@ -1634,71 +1634,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "uiActions", - "id": "def-public.CategorizeFieldContext", - "type": "Interface", - "tags": [], - "label": "CategorizeFieldContext", - "description": [], - "path": "src/plugins/ui_actions/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "uiActions", - "id": "def-public.CategorizeFieldContext.field", - "type": "Object", - "tags": [], - "label": "field", - "description": [], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewField", - "text": "DataViewField" - } - ], - "path": "src/plugins/ui_actions/public/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "uiActions", - "id": "def-public.CategorizeFieldContext.dataView", - "type": "Object", - "tags": [], - "label": "dataView", - "description": [], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - } - ], - "path": "src/plugins/ui_actions/public/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "uiActions", - "id": "def-public.CategorizeFieldContext.originatingApp", - "type": "string", - "tags": [], - "label": "originatingApp", - "description": [], - "path": "src/plugins/ui_actions/public/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "uiActions", "id": "def-public.Presentable", @@ -2300,21 +2235,6 @@ ], "enums": [], "misc": [ - { - "parentPluginId": "uiActions", - "id": "def-public.ACTION_CATEGORIZE_FIELD", - "type": "string", - "tags": [], - "label": "ACTION_CATEGORIZE_FIELD", - "description": [], - "signature": [ - "\"ACTION_CATEGORIZE_FIELD\"" - ], - "path": "src/plugins/ui_actions/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "uiActions", "id": "def-public.ACTION_VISUALIZE_FIELD", @@ -2384,21 +2304,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "uiActions", - "id": "def-public.CATEGORIZE_FIELD_TRIGGER", - "type": "string", - "tags": [], - "label": "CATEGORIZE_FIELD_TRIGGER", - "description": [], - "signature": [ - "\"CATEGORIZE_FIELD_TRIGGER\"" - ], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "uiActions", "id": "def-public.PresentableGrouping", @@ -2468,53 +2373,6 @@ } ], "objects": [ - { - "parentPluginId": "uiActions", - "id": "def-public.categorizeFieldTrigger", - "type": "Object", - "tags": [], - "label": "categorizeFieldTrigger", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "uiActions", - "id": "def-public.categorizeFieldTrigger.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "uiActions", - "id": "def-public.categorizeFieldTrigger.title", - "type": "string", - "tags": [], - "label": "title", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "uiActions", - "id": "def-public.categorizeFieldTrigger.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "path": "packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "uiActions", "id": "def-public.rowClickTrigger", diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index be0a4b19f7e6f..701ba81cde034 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.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 | |-------------------|-----------|------------------------|-----------------| -| 145 | 0 | 103 | 9 | +| 135 | 0 | 93 | 9 | ## Client diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 05b486f641567..aa18cb1591d33 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index bac6c5b3a9654..f2028b1eb4f9f 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 62395734e0705..19f9c749c44d5 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 09a1feea1ceee..0d5e841aaada5 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -635,7 +635,7 @@ }, "[] | undefined; refreshInterval?: number | undefined; iconType?: ", "IconType", - " | undefined; showQueryInput?: boolean | undefined; dataTestSubj?: string | undefined; showSaveQuery?: boolean | undefined; customSubmitButton?: React.ReactNode; dataViewPickerOverride?: React.ReactNode; screenTitle?: string | undefined; showQueryMenu?: boolean | undefined; showFilterBar?: boolean | undefined; showDatePicker?: boolean | undefined; showAutoRefreshOnly?: boolean | undefined; filtersForSuggestions?: ", + " | undefined; dataTestSubj?: string | undefined; showSaveQuery?: boolean | undefined; customSubmitButton?: React.ReactNode; dataViewPickerOverride?: React.ReactNode; screenTitle?: string | undefined; showQueryMenu?: boolean | undefined; showQueryInput?: boolean | undefined; showFilterBar?: boolean | undefined; showDatePicker?: boolean | undefined; showAutoRefreshOnly?: boolean | undefined; filtersForSuggestions?: ", { "pluginId": "@kbn/es-query", "scope": "common", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 2ca87c6b97a6e..4f482eebda4cc 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-11-22 +date: 2023-11-30 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 1eb439048b7fa..531975fc114df 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 37f78054066aa..ab2d33d5acee2 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2023-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 2727c167b65da..9ed615b1d045c 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-11-22 +date: 2023-11-30 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 350fe8585a85d..5a15363adf97f 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-11-22 +date: 2023-11-30 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 d9485520d94b8..fab000edb3eb1 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-11-22 +date: 2023-11-30 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 10bb302175485..f45b9b478bef7 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-11-22 +date: 2023-11-30 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 a51cfd3b9b7c7..d30723d79a127 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-11-22 +date: 2023-11-30 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 bbdcfa2266b02..f3f82397e2e00 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-11-22 +date: 2023-11-30 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 c14aef37a9673..d1dba2fdb2e30 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-11-22 +date: 2023-11-30 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 40111cf7a82a9..c8f89d771562f 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-11-22 +date: 2023-11-30 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 10c6c035b366d..c9d6b12adc448 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-11-22 +date: 2023-11-30 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 a98b9bac67200..407fce6e3f77e 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-11-22 +date: 2023-11-30 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 e6756b0424998..a5e415b168acb 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-11-22 +date: 2023-11-30 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 e33027a96715f..7f28cd79d6791 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-11-22 +date: 2023-11-30 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 0c33391b5d534..08f295704c990 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index b9e6229c9486b..13897de837621 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -4423,22 +4423,16 @@ "children": [ { "parentPluginId": "visualizations", - "id": "def-public.VisTypeAlias.aliasPath", - "type": "string", - "tags": [], - "label": "aliasPath", - "description": [], - "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "visualizations", - "id": "def-public.VisTypeAlias.aliasApp", - "type": "string", + "id": "def-public.VisTypeAlias.alias", + "type": "Object", "tags": [], - "label": "aliasApp", - "description": [], + "label": "alias", + "description": [ + "\nProvide `alias` when your visualization has a dedicated app for creation.\nTODO: Provide a generic callback to create visualizations inline." + ], + "signature": [ + "{ app: string; path: string; } | undefined" + ], "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", "deprecated": false, "trackAdoption": false @@ -5874,31 +5868,6 @@ "deprecated": false, "trackAdoption": false, "children": [ - { - "parentPluginId": "visualizations", - "id": "def-public.VisualizationListItem.editUrl", - "type": "string", - "tags": [], - "label": "editUrl", - "description": [], - "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "visualizations", - "id": "def-public.VisualizationListItem.editApp", - "type": "string", - "tags": [], - "label": "editApp", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "visualizations", "id": "def-public.VisualizationListItem.error", @@ -6055,6 +6024,20 @@ "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationListItem.editor", + "type": "CompoundType", + "tags": [], + "label": "editor", + "description": [], + "signature": [ + "{ editUrl: string; editApp?: string | undefined; } | { onEdit: (savedObjectId: string) => Promise; }" + ], + "path": "src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -13422,7 +13405,7 @@ "label": "seriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", "deprecated": false, @@ -15513,7 +15496,7 @@ "label": "SeriesType", "description": [], "signature": [ - "\"bar\" | \"line\" | \"area\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" + "\"area\" | \"line\" | \"bar\" | \"bar_stacked\" | \"area_stacked\" | \"bar_horizontal\" | \"bar_percentage_stacked\" | \"bar_horizontal_stacked\" | \"area_percentage_stacked\" | \"bar_horizontal_percentage_stacked\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", "deprecated": false, diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 1cbbb8e2ee75d..5882749feec76 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-11-22 +date: 2023-11-30 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 837 | 12 | 807 | 19 | +| 835 | 12 | 804 | 19 | ## Client diff --git a/catalog-info.yaml b/catalog-info.yaml index 96b4c146590a6..3e23a7e20ccef 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -54,19 +54,9 @@ spec: provider_settings: trigger_mode: none teams: - kibana-release-operators: - access_level: MANAGE_BUILD_AND_READ kibana-operations: access_level: MANAGE_BUILD_AND_READ - appex-qa: - access_level: BUILD_AND_READ - security-engineering-productivity: - access_level: BUILD_AND_READ - fleet: - access_level: BUILD_AND_READ - kibana-tech-leads: - access_level: BUILD_AND_READ - kibana-core: + kibana-release-operators: access_level: BUILD_AND_READ cloud-tooling: access_level: BUILD_AND_READ @@ -164,3 +154,31 @@ spec: access_level: MANAGE_BUILD_AND_READ everyone: access_level: READ_ONLY +--- +# yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: buildkite-pipeline-kibana-kme-test +spec: + implementation: + apiVersion: buildkite.elastic.dev/v1 + kind: Pipeline + metadata: + description: Temporary pipeline for testing Kibana KME work + name: kibana-kme-test + spec: + pipeline_file: .buildkite/scripts/pipelines/pull_request/pipeline.sh + provider_settings: + build_branches: false + build_pull_requests: true + publish_commit_status: false + trigger_mode: none + repository: elastic/kibana + teams: + kibana_operations: + access_level: MANAGE_BUILD_AND_READ + everyone: + access_level: READ_ONLY + owner: group:kibana-operations + type: buildkite-pipeline diff --git a/docs/api/alerting/enable_rule.asciidoc b/docs/api/alerting/enable_rule.asciidoc index f51f6c9295332..ba04d0147944a 100644 --- a/docs/api/alerting/enable_rule.asciidoc +++ b/docs/api/alerting/enable_rule.asciidoc @@ -6,8 +6,6 @@ Enable a rule. -WARNING: This API supports <> only. - [NOTE] ==== For the most up-to-date API details, refer to the diff --git a/docs/developer/contributing/development-accessibility-tests.asciidoc b/docs/developer/contributing/development-accessibility-tests.asciidoc index 2fe2682a3e365..491c16b8a82db 100644 --- a/docs/developer/contributing/development-accessibility-tests.asciidoc +++ b/docs/developer/contributing/development-accessibility-tests.asciidoc @@ -68,7 +68,7 @@ node scripts/functional_test_runner.js --config test/accessibility/config.ts ----------- To run the x-pack tests, swap the config file out for -`x-pack/test/accessibility/config.ts`. +`x-pack/test/accessibility/apps/{group1,group2,group3}/config.ts`. The testing is done using https://github.com/dequelabs/axe-core[axe]. You can run the same thing that runs CI using browser plugins: diff --git a/docs/developer/contributing/interpreting-ci-failures.asciidoc b/docs/developer/contributing/interpreting-ci-failures.asciidoc index 7708c866c3a81..976b3aded3653 100644 --- a/docs/developer/contributing/interpreting-ci-failures.asciidoc +++ b/docs/developer/contributing/interpreting-ci-failures.asciidoc @@ -33,7 +33,7 @@ image::images/test_results.png[Buildkite build screenshot] Looking at the failure, we first look at the Error and stack trace. In the example below, this test failed to find an element within the timeout; `Error: retry.try timeout: TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="createSpace"])` -We know the test file from the stack trace was on line 50 of `test/accessibility/apps/spaces.ts` (this test and the stack trace context is kibana/x-pack/ so the file is https://github.com/elastic/kibana/blob/main/x-pack/test/accessibility/apps/spaces.ts#L50). +We know the test file from the stack trace was on line 50 of `test/accessibility/apps/spaces.ts` (this test and the stack trace context is kibana/x-pack/ so the file is https://github.com/elastic/kibana/blob/main/x-pack/test/accessibility/apps/group1/spaces.ts#L50). The function to click on the element was called from a page object method in `test/functional/page_objects/space_selector_page.ts` https://github.com/elastic/kibana/blob/main/x-pack/test/functional/page_objects/space_selector_page.ts#L58 diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index df3f4c8ec855d..cb233fc8c10ce 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -672,7 +672,7 @@ Elastic. |This plugin allows for other plugins to add data to Kibana stack monitoring documents. -|{kib-repo}blob/{branch}/x-pack/plugins/notifications/README.md[notifications] +|{kib-repo}blob/{branch}/x-pack/plugins/notifications/README.mdx[notifications] |The Notifications plugin provides a set of services to help Solutions and plugins send notifications to users. diff --git a/docs/maps/connect-to-ems.asciidoc b/docs/maps/connect-to-ems.asciidoc index 8db34ee3f61bf..8fac6a6d95c15 100644 --- a/docs/maps/connect-to-ems.asciidoc +++ b/docs/maps/connect-to-ems.asciidoc @@ -3,17 +3,58 @@ :ems-docker-repo: docker.elastic.co/elastic-maps-service/elastic-maps-server-ubi8 :ems-docker-image: {ems-docker-repo}:{version} +:ems-headers-url: https://deployment-host https://www.elastic.co/elastic-maps-service[Elastic Maps Service (EMS)] is a service that hosts tile layers and vector shapes of administrative boundaries. If you are using Kibana's out-of-the-box settings, Maps is already configured to use EMS. +[float] +=== Domains + EMS requests are made to the following domains: -* tiles.maps.elastic.co -* vector.maps.elastic.co +* Tile Service: `tiles.maps.elastic.co` +* File Service: `vector.maps.elastic.co` + +[float] +=== Headers + +Find below examples of the request and response headers from Kibana and a minimal `curl` request example showing the response headers sent by each service. + +WARNING: These headers may change without further notice at anytime and are shared for reference. + +[float] +==== EMS Tile Service + +The EMS Tile Service provides basemaps in three different styles as the default background for Maps visualizations. The basemaps use https://www.openstreetmap.org/about[OpenStreetMap] data following the https://openmaptiles.org/[OpenMapTiles] schema and can be explored at https://maps.elastic.co[maps.elastic.co]. + +Headers for the Tile Service JSON manifest describing the basemaps available. + +include::headers/tile-json.asciidoc[] + +Headers for a vector tile asset in _protobuffer_ format from the Tile Service. + +include::headers/tile-pbf.asciidoc[] + +Headers for an sprite image asset from the Tile Service + +include::headers/tile-png.asciidoc[] + + +[float] +==== EMS File Service + +EMS File Service provides the administrative boundaries used for <> as static assets in GeoJSON or TopoJSON formats and can be explored at https://maps.elastic.co[maps.elastic.co]. + +Headers for the File Service JSON manifest that declares all the datasets available. + +include::headers/file-json.asciidoc[] + +Headers for a sample Dataset from the File Service in TopoJSON format. + +include::headers/file-data.asciidoc[] -Maps makes requests directly from the browser to EMS. [float] === Disable Elastic Maps Service diff --git a/docs/maps/headers/file-data.asciidoc b/docs/maps/headers/file-data.asciidoc new file mode 100644 index 0000000000000..62b47ed714d0a --- /dev/null +++ b/docs/maps/headers/file-data.asciidoc @@ -0,0 +1,131 @@ + +++++ +
+
+ + + +
+
+++++ +[%collapsible] +==== +[source,bash,subs="attributes"] +---------------------------------- +curl -I 'https://vector.maps.elastic.co/files/world_countries_v7.topo.json?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version={version}' \ +-H 'User-Agent: curl/7.81.0' \ +-H 'Accept: */*' \ +-H 'Accept-Encoding: gzip, deflate, br' +---------------------------------- + +Server response + +[source,regex] +---------------------------------- +HTTP/2 200 +x-guploader-uploadid: ABPtcPpmMffchVgfHIr-SSC00WORo145oV-1q0asjqRvjLV_7cIgyfLRfofXV-BG7huMYABFypblcgdgXRBARhpo2c88ow +x-goog-generation: 1689593325442971 +x-goog-metageneration: 1 +x-goog-stored-content-encoding: gzip +x-goog-stored-content-length: 587241 +content-encoding: gzip +x-goog-hash: crc32c=OcROeg== +x-goog-hash: md5=8KKIwD6wbKa3YYXTnnFcZw== +x-goog-storage-class: MULTI_REGIONAL +accept-ranges: bytes +content-length: 587241 +access-control-allow-origin: * +access-control-expose-headers: Authorization, Content-Length, Content-Type, Date, Server, Transfer-Encoding, X-GUploader-UploadID, X-Google-Trace, accept, elastic-api-version, kbn-name, kbn-version, origin +server: UploadServer +date: Tue, 21 Nov 2023 14:22:16 GMT +expires: Tue, 21 Nov 2023 15:22:16 GMT +cache-control: public, max-age=3600,no-transform +age: 2202 +last-modified: Mon, 17 Jul 2023 11:28:45 GMT +etag: "f0a288c03eb06ca6b76185d39e715c67" +content-type: application/json +alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 +---------------------------------- +==== +++++ +
+ + +
+++++ diff --git a/docs/maps/headers/file-json.asciidoc b/docs/maps/headers/file-json.asciidoc new file mode 100644 index 0000000000000..8e08508da14fc --- /dev/null +++ b/docs/maps/headers/file-json.asciidoc @@ -0,0 +1,132 @@ + +++++ +
+
+ + + +
+
+++++ +[%collapsible] +==== +[source,bash,subs="attributes"] +---------------------------------- +curl -I 'https://vector.maps.elastic.co/v{minor-version}/manifest?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version={version}' \ +-H 'User-Agent: curl/7.81.0' \ +-H 'Accept: */*' \ +-H 'Accept-Encoding: gzip, deflate, br' +---------------------------------- + +Server response + +[source,regex] +---------------------------------- +HTTP/2 200 +x-guploader-uploadid: ABPtcPp_BvMdBDO5jVlutETVHmvpOachwjilw4AkIKwMrOQJ4exR9Eln4g0LkW3V_LLSEpvjYLtUtFmO0Uwr61XXUhoP_A +x-goog-generation: 1689593295246576 +x-goog-metageneration: 1 +x-goog-stored-content-encoding: gzip +x-goog-stored-content-length: 108029 +content-encoding: gzip +x-goog-hash: crc32c=T5gVpw== +x-goog-hash: md5=6F8KWV8VTdx8FsN2iFehow== +x-goog-storage-class: MULTI_REGIONAL +accept-ranges: bytes +content-length: 108029 +access-control-allow-origin: * +access-control-expose-headers: Authorization, Content-Length, Content-Type, Date, Server, Transfer-Encoding, X-GUploader-UploadID, X-Google-Trace, accept, elastic-api-version, kbn-name, kbn-version, origin +server: UploadServer +date: Tue, 21 Nov 2023 14:25:07 GMT +expires: Tue, 21 Nov 2023 15:25:07 GMT +cache-control: public, max-age=3600,no-transform +age: 2170 +last-modified: Mon, 17 Jul 2023 11:28:15 GMT +etag: "e85f0a595f154ddc7c16c3768857a1a3" +content-type: application/json +alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 +---------------------------------- +==== +++++ +
+ + +
+++++ diff --git a/docs/maps/headers/tile-json.asciidoc b/docs/maps/headers/tile-json.asciidoc new file mode 100644 index 0000000000000..b34e82dcab3cd --- /dev/null +++ b/docs/maps/headers/tile-json.asciidoc @@ -0,0 +1,120 @@ + +++++ +
+
+ + + +
+
+++++ +[%collapsible] +==== +[source,bash,subs="attributes"] +---------------------------------- +curl -I 'https://tiles.maps.elastic.co/v{minor-version}/manifest?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version={version}' \ +-H 'User-Agent: curl/7.81.0' \ +-H 'Accept: */*' \ +-H 'Accept-Encoding: gzip, deflate, br' +---------------------------------- + +Server response + +[source,regex] +---------------------------------- +HTTP/2 200 +server: BaseHTTP/0.6 Python/3.11.4 +date: Mon, 20 Nov 2023 15:08:46 GMT +content-type: application/json; charset=utf-8 +elastic-api-version: 2023-10-31 +access-control-allow-origin: * +access-control-allow-methods: GET, OPTIONS, HEAD +access-control-allow-headers: Origin, Accept, Content-Type, kbn-version, elastic-api-version +access-control-expose-headers: etag +content-encoding: gzip +vary: Accept-Encoding +x-varnish: 844076 5416505 +accept-ranges: bytes +varnish-age: 85285 +cache-control: private, max-age=86400 +via: 1.1 varnish (Varnish/7.0), 1.1 google +alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 +---------------------------------- +==== +++++ +
+ + +
+++++ diff --git a/docs/maps/headers/tile-pbf.asciidoc b/docs/maps/headers/tile-pbf.asciidoc new file mode 100644 index 0000000000000..52215a714cda1 --- /dev/null +++ b/docs/maps/headers/tile-pbf.asciidoc @@ -0,0 +1,121 @@ + +++++ +
+
+ + + +
+
+++++ +[%collapsible] +==== +[source,bash,subs="attributes"] +---------------------------------- +$ curl -I 'https://tiles.maps.elastic.co/data/v3/1/1/0.pbf?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version={version}' \ +-H 'User-Agent: curl/7.81.0' \ +-H 'Accept: */*' \ +-H 'Accept-Encoding: gzip, deflate, br' +---------------------------------- + +Server response + +[source,regex] +---------------------------------- +HTTP/2 200 +content-encoding: gzip +content-length: 144075 +access-control-allow-origin: * +access-control-allow-methods: GET, OPTIONS, HEAD +access-control-allow-headers: Origin, Accept, Content-Type, kbn-version, elastic-api-version +access-control-expose-headers: etag +x-varnish: 3269455 5976667 +accept-ranges: bytes +varnish-age: 9045 +via: 1.1 varnish (Varnish/7.0), 1.1 google +date: Mon, 20 Nov 2023 15:08:19 GMT +age: 78827 +last-modified: Thu, 16 Sep 2021 17:14:41 GMT +etag: W/"232cb-zYEfNgd8rzHusLotRFzgRDSDDGA" +content-type: application/x-protobuf +vary: Accept-Encoding +cache-control: public,max-age=3600 +alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 +---------------------------------- +==== +++++ +
+ + +
+++++ diff --git a/docs/maps/headers/tile-png.asciidoc b/docs/maps/headers/tile-png.asciidoc new file mode 100644 index 0000000000000..9d972b1ce8c01 --- /dev/null +++ b/docs/maps/headers/tile-png.asciidoc @@ -0,0 +1,117 @@ + +++++ +
+
+ + + +
+
+++++ +[%collapsible] +==== +[source,bash] +---------------------------------- +curl -I 'https://tiles.maps.elastic.co/styles/osm-bright-desaturated/sprite.png' \ +-H 'User-Agent: curl/7.81.0' \ +-H 'Accept: image/avif,image/webp,*/*' \ +-H 'Accept-Encoding: gzip, deflate, br' +---------------------------------- + +Server response + +[source,regex] +---------------------------------- +HTTP/2 200 +content-length: 17181 +access-control-allow-origin: * +access-control-allow-methods: GET, OPTIONS, HEAD +access-control-allow-headers: Origin, Accept, Content-Type, kbn-version, elastic-api-version +access-control-expose-headers: etag +x-varnish: 8769943 4865354 +accept-ranges: bytes +varnish-age: 250 +via: 1.1 varnish (Varnish/7.0), 1.1 google +date: Tue, 21 Nov 2023 14:44:36 GMT +age: 592 +etag: W/"431d-/dqE/W5Q3FqkHikyDQtCuQqAdlY" +content-type: image/png +cache-control: public,max-age=3600 +alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 +---------------------------------- +==== +++++ +
+ + +
+++++ diff --git a/docs/setup/configuring-reporting.asciidoc b/docs/setup/configuring-reporting.asciidoc index 903f6fdfb5afd..d8ff9e1b202b6 100644 --- a/docs/setup/configuring-reporting.asciidoc +++ b/docs/setup/configuring-reporting.asciidoc @@ -113,7 +113,7 @@ Granting the privilege to generate reports also grants the user the privilege to ==== Grant access with the role API With <> enabled in Reporting, you can also use the {ref}/security-api-put-role.html[role API] to grant access to the {report-features}, using *All* privileges, or sub-feature privileges. -NOTE: this [API request](https://www.elastic.co/guide/en/kibana/current/role-management-api-put.html) needs to be executed against the Kibana API endpoint +NOTE: This link:https://www.elastic.co/guide/en/kibana/current/role-management-api-put.html[API request] needs to be executed against the link:https://www.elastic.co/guide/en/kibana/current/api.html[Kibana API endpoint]. [source, sh] --------------------------------------------------------------- POST :/api/_security/role/custom_reporting_user @@ -229,3 +229,5 @@ For more information, see {ref}/notification-settings.html#ssl-notification-sett . Add one or more users who have access to the {report-features}. + Once you've enabled SSL for {kib}, all requests to the reporting endpoints must include valid credentials. + +For more information on sharing reports, direct links, and more, refer to <>. diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index fef9ae71a085b..da4784f7ea36e 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -54,9 +54,9 @@ Details for each programming language library that Elastic provides are in the https://www.elastic.co/guide/en/elasticsearch/client/index.html[{es} Client documentation]. If you are running {kib} on our hosted {es} Service, -click *Endpoints* on the *Integrations* view +click *Connection details* on the *Integrations* view to verify your {es} endpoint and Cloud ID, and create API keys for integration. -Alternatively, the *Endpoints* are also accessible through the top bar help menu. +Alternatively, the *Connection details* are also accessible through the top bar help menu. [float] === Add sample data diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 9d38b5162f124..37bb26ae53a38 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -323,12 +323,12 @@ run {kib} in different modes. Valid options are `background_tasks` and `ui`, or `*` to select all roles. *Default: `*`* `notifications.connectors.default.email`:: -Specifies the name of the connector that is used to send email notifications. -In {ecloud}, the default value is `elastic-cloud-email`. + Choose the default email connector for user notifications. As of `8.6.0`, {kib} is shipping with a new notification mechanism that will send email notifications for various user actions, e.g. assigning a _Case_ to a user. To enable notifications, an email connector must be <> in the system via `kibana.yml`, and the notifications plugin must be configured to point to the ID of that connector. [[path-data]] `path.data`:: -The path where {kib} stores persistent data -not saved in {es}. *Default: `data`* +The path where {kib} stores persistent data not saved in {es}. +Can be a relative or absolute path. +Relative paths are resolved starting from the installation directory. *Default: `data`* `pid.file`:: Specifies the path where {kib} creates the process ID file. diff --git a/docs/user/alerting/images/es-query-rule-conditions.png b/docs/user/alerting/images/es-query-rule-conditions.png index c9572afc3dc26..b2a4aeba332d3 100644 Binary files a/docs/user/alerting/images/es-query-rule-conditions.png and b/docs/user/alerting/images/es-query-rule-conditions.png differ diff --git a/docs/user/alerting/images/rule-types-es-query-conditions.png b/docs/user/alerting/images/rule-types-es-query-conditions.png index 47eed98caf5e6..786d172368868 100644 Binary files a/docs/user/alerting/images/rule-types-es-query-conditions.png and b/docs/user/alerting/images/rule-types-es-query-conditions.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-conditions.png b/docs/user/alerting/images/rule-types-index-threshold-conditions.png index 9aac7bd26c3c0..5c540a0790489 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-conditions.png and b/docs/user/alerting/images/rule-types-index-threshold-conditions.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-example-aggregation.png b/docs/user/alerting/images/rule-types-index-threshold-example-aggregation.png index 68b21a62d8a9b..5367d05be86b6 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-example-aggregation.png and b/docs/user/alerting/images/rule-types-index-threshold-example-aggregation.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-example-grouping.png b/docs/user/alerting/images/rule-types-index-threshold-example-grouping.png index aaed0acfbf863..1f6a6003353e7 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-example-grouping.png and b/docs/user/alerting/images/rule-types-index-threshold-example-grouping.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-example-index.png b/docs/user/alerting/images/rule-types-index-threshold-example-index.png index 29a9c9520d69e..4899bd33dc3cc 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-example-index.png and b/docs/user/alerting/images/rule-types-index-threshold-example-index.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-example-threshold.png b/docs/user/alerting/images/rule-types-index-threshold-example-threshold.png index 5a1bc35bd22c9..1f8ebc120f008 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-example-threshold.png and b/docs/user/alerting/images/rule-types-index-threshold-example-threshold.png differ diff --git a/docs/user/alerting/images/rule-types-index-threshold-select.png b/docs/user/alerting/images/rule-types-index-threshold-select.png index 3a00f5da6aa1a..98866694e6e36 100644 Binary files a/docs/user/alerting/images/rule-types-index-threshold-select.png and b/docs/user/alerting/images/rule-types-index-threshold-select.png differ diff --git a/docs/user/alerting/rule-types/index-threshold.asciidoc b/docs/user/alerting/rule-types/index-threshold.asciidoc index 9945b58df8bb7..867dbf4ddb307 100644 --- a/docs/user/alerting/rule-types/index-threshold.asciidoc +++ b/docs/user/alerting/rule-types/index-threshold.asciidoc @@ -15,21 +15,37 @@ In *{stack-manage-app}* > *{rules-ui}*, click *Create rule*, fill in the name an [float] === Define the conditions +When you create an index threshold rule, you must define the conditions for the rule to detect. For example: + [role="screenshot"] image::user/alerting/images/rule-types-index-threshold-conditions.png[Defining index threshold rule conditions in {kib}] // NOTE: This is an autogenerated screenshot. Do not edit it directly. -When you create an index threshold rule, you must define the conditions for the rule to detect. For example: +1. Specify the indices to query and a time field that will be used for the time window. + +2. In the `WHEN` clause, specify how to calculate the value that is compared to the threshold. +The value is calculated by aggregating a numeric field in a 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. + +3. In the `OVER` or `GROUPED OVER` clause, specify whether the aggregation is applied over all documents or split into groups using a grouping field. +If grouping is used, an alert 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. + +4. Choose a threshold value and a comparison operator (`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. + +5. In the `FOR THE LAST` clause, specify a time window. +It determines how far back to search for documents and uses the time field set in the index clause. + +6. Optionally add a KQL expression to further refine the conditions that the rule detects. -Select an index:: -Index::: Specify the indices to query and a time field that will be used for the time window. -When::: Specify how to calculate the value that is compared to the threshold. The value is calculated by aggregating a numeric field in a 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::: Specify whether the aggregation is applied over all documents or 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. -Define the condition:: -This section defines a threshold value and a comparison operator (`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. -It also defines a time window, which determines how far back to search for documents, using the time field set in the index clause. Generally this value should be a value higher than the check interval to avoid gaps in detection. +7. Set the check interval, which defines how often to evaluate the rule conditions. +Generally this value should be set to a value that is smaller than the time window, 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. +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. [float] [[actions-index-threshold]] @@ -116,7 +132,7 @@ image::user/alerting/images/rule-types-index-threshold-example-aggregation.png[C image::user/alerting/images/rule-types-index-threshold-example-grouping.png[Choosing the groups] // NOTE: This is an autogenerated screenshot. Do not edit it directly. -.. Define the condition. To trigger the rule when any of the top four sites exceeds 420,000 bytes over a 24 hour period, select `is above` and enter `420000`. Then click *For the last*, enter `24`, and select `hours`. +.. To trigger the rule when any of the top four sites exceeds 420,000 bytes over a 24 hour period, select `is above` and enter `420000`. Then click *For the last*, enter `24`, and select `hours`. + [role="screenshot"] image::user/alerting/images/rule-types-index-threshold-example-threshold.png[Setting the threshold] @@ -129,7 +145,7 @@ image::user/alerting/images/rule-types-index-threshold-example-threshold.png[Set image::user/alerting/images/rule-types-index-threshold-example-preview.png[Setting the check interval] // NOTE: This is an autogenerated screenshot. Do not edit it directly. -The preview chart will render showing the 24 hour sum of bytes at 4 hours intervals (the _check interval_) for the past 120 hours (the last 30 intervals). +The preview chart will render showing the 24 hour sum of bytes at 4 hours intervals for the past 120 hours (the last 30 intervals). -- .. Change the time window and observe the effect it has on the chart. Compare a 24 window to a 12 hour window. Notice the variability in the sum of bytes, due to different traffic levels during the day compared to at night. This variability would result in noisy rules, so the 24 hour window is better. The preview chart can help you find the right values for your rule. diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 8d53018dec572..676fb430b9c66 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -31,6 +31,8 @@ You access the options from the *Share* menu in the toolbar. The sharing options NOTE: For Elastic Cloud deployments, {kib} instances require a minimum of 2GB RAM to generate PDF or PNG reports. To change {kib} sizing, {ess-console}[edit the deployment]. +For more information on how to configure reporting in {kib}, refer to <> + [float] [[manually-generate-reports]] == Create reports diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index c85c804cdfda4..56517c4136f7f 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -425,6 +425,9 @@ { "id": "kibNewsfeedPluginApi" }, + { + "id": "kibNotificationsPluginApi" + }, { "id": "kibObservabilityPluginApi" }, diff --git a/package.json b/package.json index 73ae2cc388543..f7ca215f20fd6 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.9.1-canary.1", "@elastic/ems-client": "8.5.1", - "@elastic/eui": "90.0.0", + "@elastic/eui": "90.0.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -167,6 +167,7 @@ "@kbn/bfetch-explorer-plugin": "link:examples/bfetch_explorer", "@kbn/bfetch-plugin": "link:src/plugins/bfetch", "@kbn/calculate-auto": "link:packages/kbn-calculate-auto", + "@kbn/calculate-width-from-char-count": "link:packages/kbn-calculate-width-from-char-count", "@kbn/canvas-plugin": "link:x-pack/plugins/canvas", "@kbn/cases-api-integration-test-plugin": "link:x-pack/test/cases_api_integration/common/plugins/cases", "@kbn/cases-components": "link:packages/kbn-cases-components", @@ -535,8 +536,10 @@ "@kbn/ml-anomaly-utils": "link:x-pack/packages/ml/anomaly_utils", "@kbn/ml-category-validator": "link:x-pack/packages/ml/category_validator", "@kbn/ml-chi2test": "link:x-pack/packages/ml/chi2test", + "@kbn/ml-creation-wizard-utils": "link:x-pack/packages/ml/creation_wizard_utils", "@kbn/ml-data-frame-analytics-utils": "link:x-pack/packages/ml/data_frame_analytics_utils", "@kbn/ml-data-grid": "link:x-pack/packages/ml/data_grid", + "@kbn/ml-data-view-utils": "link:x-pack/packages/ml/data_view_utils", "@kbn/ml-date-picker": "link:x-pack/packages/ml/date_picker", "@kbn/ml-date-utils": "link:x-pack/packages/ml/date_utils", "@kbn/ml-error-utils": "link:x-pack/packages/ml/error_utils", @@ -580,6 +583,7 @@ "@kbn/osquery-plugin": "link:x-pack/plugins/osquery", "@kbn/paertial-results-example-plugin": "link:examples/partial_results_example", "@kbn/painless-lab-plugin": "link:x-pack/plugins/painless_lab", + "@kbn/panel-loader": "link:packages/kbn-panel-loader", "@kbn/portable-dashboards-example": "link:examples/portable_dashboards_example", "@kbn/preboot-example-plugin": "link:examples/preboot_example", "@kbn/presentation-util-plugin": "link:src/plugins/presentation_util", @@ -646,6 +650,9 @@ "@kbn/search-response-warnings": "link:packages/kbn-search-response-warnings", "@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler", "@kbn/security-plugin": "link:x-pack/plugins/security", + "@kbn/security-plugin-types-common": "link:x-pack/packages/security/plugin_types_common", + "@kbn/security-plugin-types-public": "link:x-pack/packages/security/plugin_types_public", + "@kbn/security-plugin-types-server": "link:x-pack/packages/security/plugin_types_server", "@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess", "@kbn/security-solution-features": "link:x-pack/packages/security-solution/features", "@kbn/security-solution-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/security_solution", @@ -750,7 +757,6 @@ "@kbn/status-plugin-a-plugin": "link:test/server_integration/plugins/status_plugin_a", "@kbn/status-plugin-b-plugin": "link:test/server_integration/plugins/status_plugin_b", "@kbn/std": "link:packages/kbn-std", - "@kbn/subscription-tracking": "link:packages/kbn-subscription-tracking", "@kbn/synthetics-plugin": "link:x-pack/plugins/synthetics", "@kbn/task-manager-fixture-plugin": "link:x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture", "@kbn/task-manager-performance-plugin": "link:x-pack/test/plugin_api_perf/plugins/task_manager_performance", @@ -956,7 +962,8 @@ "jsonwebtoken": "^9.0.0", "jsts": "^1.6.2", "kea": "^2.6.0", - "langchain": "^0.0.151", + "langchain": "^0.0.186", + "langsmith": "^0.0.48", "launchdarkly-js-client-sdk": "^3.1.4", "launchdarkly-node-server-sdk": "^7.0.3", "load-json-file": "^6.2.0", @@ -1008,7 +1015,7 @@ "query-string": "^6.13.2", "rbush": "^3.0.1", "re-resizable": "^6.9.9", - "re2": "1.20.1", + "re2": "1.20.9", "react": "^17.0.2", "react-ace": "^7.0.5", "react-color": "^2.13.8", @@ -1238,6 +1245,8 @@ "@kbn/managed-vscode-config": "link:packages/kbn-managed-vscode-config", "@kbn/managed-vscode-config-cli": "link:packages/kbn-managed-vscode-config-cli", "@kbn/management-storybook-config": "link:packages/kbn-management/storybook/config", + "@kbn/mock-idp-plugin": "link:packages/kbn-mock-idp-plugin", + "@kbn/openapi-bundler": "link:packages/kbn-openapi-bundler", "@kbn/openapi-generator": "link:packages/kbn-openapi-generator", "@kbn/optimizer": "link:packages/kbn-optimizer", "@kbn/optimizer-webpack-helpers": "link:packages/kbn-optimizer-webpack-helpers", diff --git a/packages/cloud/deployment_details/deployment_details_modal.tsx b/packages/cloud/deployment_details/deployment_details_modal.tsx index c715b2d830c85..4064d289bf680 100644 --- a/packages/cloud/deployment_details/deployment_details_modal.tsx +++ b/packages/cloud/deployment_details/deployment_details_modal.tsx @@ -38,8 +38,8 @@ export const DeploymentDetailsModal: FC = ({ closeModal }) => { > - {i18n.translate('cloud.deploymentDetails.helpMenuLinks.endpoints', { - defaultMessage: 'Endpoints', + {i18n.translate('cloud.deploymentDetails.helpMenuLinks.connectionDetails', { + defaultMessage: 'Connection details', })} diff --git a/packages/content-management/table_list_view_table/src/components/item_details.tsx b/packages/content-management/table_list_view_table/src/components/item_details.tsx index 0b2f52b216905..6c14c9c52021d 100644 --- a/packages/content-management/table_list_view_table/src/components/item_details.tsx +++ b/packages/content-management/table_list_view_table/src/components/item_details.tsx @@ -57,7 +57,7 @@ export function ItemDetails({ ); const onClickTitleHandler = useMemo(() => { - if (!onClickTitle) { + if (!onClickTitle || getDetailViewLink?.(item)) { return undefined; } @@ -65,7 +65,7 @@ export function ItemDetails({ e.preventDefault(); onClickTitle(item); }) as React.MouseEventHandler; - }, [item, onClickTitle]); + }, [item, onClickTitle, getDetailViewLink]); const renderTitle = useCallback(() => { const href = getDetailViewLink ? getDetailViewLink(item) : undefined; @@ -79,7 +79,7 @@ export function ItemDetails({ {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index ccf6ef791d8d4..f7de8935ea949 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -289,12 +289,6 @@ function TableListViewTableComp({ ); } - if (getDetailViewLink && onClickTitle) { - throw new Error( - `[TableListView] Either "getDetailViewLink" or "onClickTitle" can be provided. Not both.` - ); - } - if (contentEditor.isReadonly === false && contentEditor.onSave === undefined) { throw new Error( `[TableListView] A value for [contentEditor.onSave()] must be provided when [contentEditor.isReadonly] is false.` diff --git a/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts b/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts index 45b466225c375..ed6cc186313f3 100644 --- a/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts +++ b/packages/core/apps/core-apps-browser-internal/src/status/lib/load_status.test.ts @@ -76,6 +76,8 @@ const mockedResponse: StatusResponse = { total_in_bytes: 0, }, resident_set_size_in_bytes: 1, + array_buffers_in_bytes: 1, + external_in_bytes: 1, }, event_loop_delay: 1, event_loop_delay_histogram: mocked.createHistogram(), @@ -96,6 +98,8 @@ const mockedResponse: StatusResponse = { total_in_bytes: 0, }, resident_set_size_in_bytes: 1, + array_buffers_in_bytes: 1, + external_in_bytes: 1, }, event_loop_delay: 1, event_loop_delay_histogram: mocked.createHistogram(), diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts index fded446938e75..473ec16369266 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.test.ts @@ -44,18 +44,18 @@ describe('breadcrumbs', () => { { id: 'root', title: 'Root', - path: ['root'], + path: 'root', breadcrumbStatus: 'hidden' as 'hidden', children: [ { id: 'subNav', - path: ['root', 'subNav'], + path: 'root.subNav', title: '', // intentionally empty to skip rendering children: [ { id: 'navItem1', title: 'Nav Item 1', - path: ['root', 'subNav', 'navItem1'], + path: 'root.subNav.navItem1', deepLink: { id: 'navItem1', title: 'Nav Item 1', @@ -318,12 +318,12 @@ describe('getActiveNodes$()', () => { { id: 'root', title: 'Root', - path: ['root'], + path: 'root', children: [ { id: 'item1', title: 'Item 1', - path: ['root', 'item1'], + path: 'root.item1', deepLink: { id: 'item1', title: 'Item 1', @@ -344,14 +344,12 @@ describe('getActiveNodes$()', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item1', title: 'Item 1', - isActive: true, - path: ['root', 'item1'], + path: 'root.item1', deepLink: { id: 'item1', title: 'Item 1', @@ -375,12 +373,12 @@ describe('getActiveNodes$()', () => { { id: 'root', title: 'Root', - path: ['root'], + path: 'root', children: [ { id: 'item1', title: 'Item 1', - path: ['root', 'item1'], + path: 'root.item1', getIsActive: () => true, }, ], @@ -395,14 +393,12 @@ describe('getActiveNodes$()', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item1', title: 'Item 1', - isActive: true, - path: ['root', 'item1'], + path: 'root.item1', getIsActive: expect.any(Function), }, ], diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.test.ts index 93abfd5d5a1f7..a207162e060cb 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.test.ts @@ -23,27 +23,27 @@ describe('flattenNav', () => { { id: 'root', title: 'Root', - path: ['root'], + path: 'root', children: [ { id: 'item1', title: 'Item 1', - path: ['root', 'item1'], + path: 'root.item1', }, { id: 'item2', title: 'Item 2', - path: ['root', 'item2'], + path: 'root.item2', }, { id: 'group1', title: 'Group 1', - path: ['root', 'group1'], + path: 'root.group1', children: [ { id: 'item3', title: 'Item 3', - path: ['root', 'group1', 'item3'], + path: 'root.group1.item3', }, ], }, @@ -55,27 +55,27 @@ describe('flattenNav', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][0]': { id: 'item1', title: 'Item 1', - path: ['root', 'item1'], + path: 'root.item1', }, '[0][1]': { id: 'item2', title: 'Item 2', - path: ['root', 'item2'], + path: 'root.item2', }, '[0][2]': { id: 'group1', title: 'Group 1', - path: ['root', 'group1'], + path: 'root.group1', }, '[0][2][0]': { id: 'item3', title: 'Item 3', - path: ['root', 'group1', 'item3'], + path: 'root.group1.item3', }, }; @@ -89,18 +89,18 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][0]': { id: 'group1', title: 'Group 1', - path: ['root', 'group1'], + path: 'root.group1', }, '[0][0][0]': { id: 'item1', title: 'Item 1', deepLink: getDeepLink('item1', 'item1'), - path: ['root', 'group1', 'item1'], + path: 'root.group1.item1', }, }; @@ -109,21 +109,18 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'group1', title: 'Group 1', - isActive: true, - path: ['root', 'group1'], + path: 'root.group1', }, { id: 'item1', title: 'Item 1', - isActive: true, deepLink: getDeepLink('item1', 'item1'), - path: ['root', 'group1', 'item1'], + path: 'root.group1.item1', }, ], ]); @@ -134,35 +131,35 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][0]': { id: 'group1', title: 'Group 1', deepLink: getDeepLink('group1', 'group1'), - path: ['root', 'group1'], + path: 'root.group1', }, '[0][0][0]': { id: 'group1A', title: 'Group 1A', - path: ['root', 'group1', 'group1A'], + path: 'root.group1.group1A', }, '[0][0][0][0]': { id: 'item1', title: 'Item 1', deepLink: getDeepLink('item1', 'item1'), - path: ['root', 'group1', 'group1A', 'item1'], + path: 'root.group1.group1A.item1', }, '[0][1]': { id: 'group2', title: 'Group 2', - path: ['root', 'group2'], + path: 'root.group2', }, '[0][1][0]': { id: 'item2', title: 'Item 2', deepLink: getDeepLink('item1', 'item1'), // Same link as above, should match both - path: ['root', 'group2', 'item2'], + path: 'root.group2.item2', }, }; @@ -172,49 +169,42 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'group1', title: 'Group 1', - isActive: true, deepLink: getDeepLink('group1', 'group1'), - path: ['root', 'group1'], + path: 'root.group1', }, { id: 'group1A', title: 'Group 1A', - isActive: true, - path: ['root', 'group1', 'group1A'], + path: 'root.group1.group1A', }, { id: 'item1', title: 'Item 1', - isActive: true, deepLink: getDeepLink('item1', 'item1'), - path: ['root', 'group1', 'group1A', 'item1'], + path: 'root.group1.group1A.item1', }, ], [ { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'group2', title: 'Group 2', - isActive: true, - path: ['root', 'group2'], + path: 'root.group2', }, { id: 'item2', title: 'Item 2', - isActive: true, deepLink: getDeepLink('item1', 'item1'), - path: ['root', 'group2', 'item2'], + path: 'root.group2.item2', }, ], ]); @@ -225,13 +215,13 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][1]': { id: 'item1', title: 'Item 1', deepLink: getDeepLink('item1', `item1#/foo/bar`), - path: ['root', 'item1'], + path: 'root.item1', }, }; @@ -240,15 +230,13 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item1', title: 'Item 1', - isActive: true, deepLink: getDeepLink('item1', `item1#/foo/bar`), - path: ['root', 'item1'], + path: 'root.item1', }, ], ]); @@ -260,7 +248,7 @@ describe('findActiveNodes', () => { id: 'root', title: 'Root', deepLink: getDeepLink('root', `root`), - path: ['root'], + path: 'root', }, }; @@ -269,9 +257,8 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, deepLink: getDeepLink('root', `root`), - path: ['root'], + path: 'root', }, ], ]); @@ -282,19 +269,19 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][1]': { id: 'item1', title: 'Item 1', deepLink: getDeepLink('item1', `item1#/foo`), - path: ['root', 'item1'], + path: 'root.item1', }, '[0][2]': { id: 'item2', title: 'Item 2', deepLink: getDeepLink('item2', `item1#/foo/bar`), // Should match this one - path: ['root', 'item2'], + path: 'root.item2', }, }; @@ -303,15 +290,13 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item2', title: 'Item 2', - isActive: true, deepLink: getDeepLink('item2', `item1#/foo/bar`), - path: ['root', 'item2'], + path: 'root.item2', }, ], ]); @@ -322,13 +307,13 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][1]': { id: 'item1', title: 'Item 1', deepLink: getDeepLink('item1', `appRoot`), - path: ['root', 'item1'], + path: 'root.item1', }, }; @@ -337,15 +322,13 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item1', title: 'Item 1', - isActive: true, deepLink: getDeepLink('item1', `appRoot`), - path: ['root', 'item1'], + path: 'root.item1', }, ], ]; @@ -362,19 +345,19 @@ describe('findActiveNodes', () => { '[0]': { id: 'root', title: 'Root', - path: ['root'], + path: 'root', }, '[0][1]': { id: 'item1', title: 'Item 1', - path: ['root', 'item1'], + path: 'root.item1', getIsActive: ({ location }) => location.pathname.startsWith('/foo'), // Should match }, '[0][2]': { id: 'item2', title: 'Item 2', deepLink: getDeepLink('item2', 'item2'), // Should match - path: ['root', 'item2'], + path: 'root.item2', }, }; @@ -393,30 +376,26 @@ describe('findActiveNodes', () => { { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item1', title: 'Item 1', - isActive: true, getIsActive: expect.any(Function), - path: ['root', 'item1'], + path: 'root.item1', }, ], [ { id: 'root', title: 'Root', - isActive: true, - path: ['root'], + path: 'root', }, { id: 'item2', title: 'Item 2', - isActive: true, deepLink: getDeepLink('item2', 'item2'), - path: ['root', 'item2'], + path: 'root.item2', }, ], ]); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.ts index 48158025414cb..63f7f8e612c2e 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/utils.ts @@ -114,7 +114,6 @@ export const findActiveNodes = ( const activeNodeFromKey = (key: string): ChromeProjectNavigationNode => ({ ...navTree[key], - isActive: true, }); Object.entries(navTree).forEach(([key, node]) => { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 30fe35be0b551..80f108c8be784 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -62,6 +62,17 @@ const getHeaderCss = ({ size }: EuiThemeComputed) => ({ top: 2px; `, }, + leftHeaderSection: css` + // needed to enable breadcrumbs truncation + min-width: 0; + flex-shrink: 1; + `, + breadcrumbsSectionItem: css` + min-width: 0; // needed to enable breadcrumbs truncation + `, + redirectAppLinksContainer: css` + min-width: 0; // needed to enable breadcrumbs truncation + `, }); type HeaderCss = ReturnType; @@ -181,7 +192,7 @@ export const ProjectHeader = ({
- + {children} @@ -196,8 +207,11 @@ export const ProjectHeader = ({ /> - - + + diff --git a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts index b879cf6f716d7..a43e8cbee8a92 100644 --- a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts +++ b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts @@ -169,7 +169,7 @@ export interface ChromeProjectNavigationNode extends NodeDefinitionBase { /** Optional title. If not provided and a "link" is provided the title will be the Deep link title */ title: string; /** Path in the tree of the node */ - path: string[]; + path: string; /** App id or deeplink id */ deepLink?: ChromeNavLink; /** @@ -178,9 +178,14 @@ export interface ChromeProjectNavigationNode extends NodeDefinitionBase { */ children?: ChromeProjectNavigationNode[]; /** - * Flag to indicate if the node is currently active. + * Handler to render the node item with custom JSX. This handler is added to render the `children` of + * the Navigation.Item component when React components are used to declare the navigation tree. */ - isActive?: boolean; + renderItem?: () => React.ReactNode; + /** + * Flag to indicate if the node is an "external" cloud link + */ + isElasticInternalLink?: boolean; } /** @public */ diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts index 45d88445df419..039f1ff8ccb86 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.test.ts @@ -40,7 +40,7 @@ test('set correct defaults', () => { "idleSocketTimeout": "PT1M", "ignoreVersionMismatch": false, "maxIdleSockets": 256, - "maxSockets": Infinity, + "maxSockets": 800, "password": undefined, "pingTimeout": "PT30S", "requestHeadersWhitelist": Array [ diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts index b1a4712a7949d..d7f426ec28e53 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/elasticsearch_config.ts @@ -40,7 +40,7 @@ export const configSchema = schema.object({ hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], { defaultValue: 'http://localhost:9200', }), - maxSockets: schema.number({ defaultValue: Infinity, min: 1 }), + maxSockets: schema.number({ defaultValue: 800, min: 1 }), maxIdleSockets: schema.number({ defaultValue: 256, min: 1 }), idleSocketTimeout: schema.duration({ defaultValue: '60s' }), compression: schema.boolean({ defaultValue: false }), diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/ensure_es_version.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/ensure_es_version.test.ts index dccb2722309c4..ec22185a29626 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/ensure_es_version.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/ensure_es_version.test.ts @@ -42,6 +42,12 @@ describe('mapNodesVersionCompatibility', () => { return { nodes: { 'node-without-http': { version, ip: 'ip' } } } as any; } + it('returns isCompatible=false with a single node with non-SemVer-compliant version', async () => { + const nodesInfo = createNodes('615c621a8416c444941dc97b142a0122d5c878d0'); + const result = await mapNodesVersionCompatibility(nodesInfo, KIBANA_VERSION, false); + expect(result.isCompatible).toBe(false); + }); + it('returns isCompatible=true with a single node that matches', async () => { const nodesInfo = createNodes('5.1.0'); const result = await mapNodesVersionCompatibility(nodesInfo, KIBANA_VERSION, false); diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.test.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.test.ts index 3aa93435982dc..867b843c1aaef 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.test.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.test.ts @@ -22,6 +22,12 @@ describe('plugins/elasticsearch', () => { it('when majors are equal, but ES minor is less than Kibana minor', () => { expect(esVersionCompatibleWithKibana('1.0.0', '1.1.0')).toBe(false); }); + + it('ES is not SemVer-compliant', () => { + expect( + esVersionCompatibleWithKibana('615c621a8416c444941dc97b142a0122d5c878d0', '1.1.0') + ).toBe(false); + }); }); describe('returns true', () => { diff --git a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.ts b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.ts index accf4edc0226e..8732d0df2fb4a 100644 --- a/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.ts +++ b/packages/core/elasticsearch/core-elasticsearch-server-internal/src/version_check/es_kibana_version_compatability.ts @@ -14,6 +14,10 @@ import semver, { coerce } from 'semver'; * 2. Older versions of ES won't work with newer versions of Kibana. */ export function esVersionCompatibleWithKibana(esVersion: string, kibanaVersion: string) { + if (!semver.valid(esVersion)) { + return false; + } + const esVersionNumbers = { major: semver.major(esVersion), minor: semver.minor(esVersion), diff --git a/packages/core/http/core-http-server-internal/src/http_config.ts b/packages/core/http/core-http-server-internal/src/http_config.ts index 201770eba2693..eba98e725e804 100644 --- a/packages/core/http/core-http-server-internal/src/http_config.ts +++ b/packages/core/http/core-http-server-internal/src/http_config.ts @@ -178,9 +178,10 @@ const configSchema = schema.object( versioned: schema.object({ /** - * Which handler resolution algo to use: "newest" or "oldest". + * Which handler resolution algo to use for public routes: "newest" or "oldest". * - * @note in development we have an additional option "none" which is also the default. + * @note Internal routes always require a version to be specified. + * @note in development we have an additional option "none" which is also the default in dev. * This prevents any fallbacks and requires that a version specified. * Useful for ensuring that a given client always specifies a version. */ diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.mocks.ts b/packages/core/http/core-http-server-internal/src/http_server.test.mocks.ts new file mode 100644 index 0000000000000..6b0eadee96f31 --- /dev/null +++ b/packages/core/http/core-http-server-internal/src/http_server.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. + */ + +export const setTlsConfigMock = jest.fn(); + +jest.doMock('@kbn/server-http-tools', () => { + const actual = jest.requireActual('@kbn/server-http-tools'); + return { + ...actual, + setTlsConfig: setTlsConfigMock, + createServer: jest.fn(actual.createServer), + }; +}); diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts index f70b035c5f22f..d50156df94ede 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts @@ -6,20 +6,12 @@ * Side Public License, v 1. */ -jest.mock('@kbn/server-http-tools', () => { - const module = jest.requireActual('@kbn/server-http-tools'); - return { - ...module, - createServer: jest.fn(module.createServer), - }; -}); - +import { setTlsConfigMock } from './http_server.test.mocks'; import { Server } from 'http'; import { rm, mkdtemp, readFile, writeFile } from 'fs/promises'; import supertest from 'supertest'; import { omit } from 'lodash'; import { join } from 'path'; - import { ByteSizeValue, schema } from '@kbn/config-schema'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import type { @@ -37,7 +29,7 @@ import { HttpServer } from './http_server'; import { Readable } from 'stream'; import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import moment from 'moment'; -import { of } from 'rxjs'; +import { of, Observable, BehaviorSubject } from 'rxjs'; const routerOptions: RouterOptions = { isDev: false, @@ -52,8 +44,12 @@ const cookieOptions = { }; let server: HttpServer; + let config: HttpConfig; +let config$: Observable; + let configWithSSL: HttpConfig; +let configWithSSL$: Observable; const loggingService = loggingSystemMock.create(); const logger = loggingService.get(); @@ -85,6 +81,7 @@ beforeEach(() => { cdn: {}, shutdownTimeout: moment.duration(500, 'ms'), } as any; + config$ = of(config); configWithSSL = { ...config, @@ -97,6 +94,7 @@ beforeEach(() => { redirectHttpFromPort: config.port + 1, }, } as HttpConfig; + configWithSSL$ = of(configWithSSL); server = new HttpServer(loggingService, 'tests', of(config.shutdownTimeout)); }); @@ -109,7 +107,7 @@ afterEach(async () => { test('log listening address after started', async () => { expect(server.isListening()).toBe(false); - await server.setup(config); + await server.setup({ config$ }); await server.start(); expect(server.isListening()).toBe(true); @@ -125,7 +123,7 @@ test('log listening address after started', async () => { test('log listening address after started when configured with BasePath and rewriteBasePath = false', async () => { expect(server.isListening()).toBe(false); - await server.setup({ ...config, basePath: '/bar', rewriteBasePath: false }); + await server.setup({ config$: of({ ...config, basePath: '/bar', rewriteBasePath: false }) }); await server.start(); expect(server.isListening()).toBe(true); @@ -141,7 +139,7 @@ test('log listening address after started when configured with BasePath and rewr test('log listening address after started when configured with BasePath and rewriteBasePath = true', async () => { expect(server.isListening()).toBe(false); - await server.setup({ ...config, basePath: '/bar', rewriteBasePath: true }); + await server.setup({ config$: of({ ...config, basePath: '/bar', rewriteBasePath: true }) }); await server.start(); expect(server.isListening()).toBe(true); @@ -157,7 +155,7 @@ test('log listening address after started when configured with BasePath and rewr test('does not allow router registration after server is listening', async () => { expect(server.isListening()).toBe(false); - const { registerRouter } = await server.setup(config); + const { registerRouter } = await server.setup({ config$ }); const router1 = new Router('/foo', logger, enhanceWithContext, routerOptions); expect(() => registerRouter(router1)).not.toThrowError(); @@ -175,7 +173,7 @@ test('does not allow router registration after server is listening', async () => test('allows router registration after server is listening via `registerRouterAfterListening`', async () => { expect(server.isListening()).toBe(false); - const { registerRouterAfterListening } = await server.setup(config); + const { registerRouterAfterListening } = await server.setup({ config$ }); const router1 = new Router('/foo', logger, enhanceWithContext, routerOptions); expect(() => registerRouterAfterListening(router1)).not.toThrowError(); @@ -205,7 +203,7 @@ test('valid params', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -235,7 +233,7 @@ test('invalid params', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -270,7 +268,7 @@ test('valid query', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -300,7 +298,7 @@ test('invalid query', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -335,7 +333,7 @@ test('valid body', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -373,7 +371,7 @@ test('valid body with validate function', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -416,7 +414,7 @@ test('not inline validation - specifying params', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -459,7 +457,7 @@ test('not inline validation - specifying validation handler', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -509,7 +507,7 @@ test('not inline handler - KibanaRequest', async () => { handler ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -558,7 +556,7 @@ test('not inline handler - RequestHandler', async () => { handler ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -592,7 +590,7 @@ test('invalid body', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -627,7 +625,7 @@ test('handles putting', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -658,7 +656,7 @@ test('handles deleting', async () => { } ); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); registerRouter(router); await server.start(); @@ -688,7 +686,9 @@ describe('with `basepath: /bar` and `rewriteBasePath: false`', () => { res.ok({ body: 'value:/foo' }) ); - const { registerRouter, server: innerServer } = await server.setup(configWithBasePath); + const { registerRouter, server: innerServer } = await server.setup({ + config$: of(configWithBasePath), + }); registerRouter(router); await server.start(); @@ -743,7 +743,9 @@ describe('with `basepath: /bar` and `rewriteBasePath: true`', () => { res.ok({ body: 'value:/foo' }) ); - const { registerRouter, server: innerServer } = await server.setup(configWithBasePath); + const { registerRouter, server: innerServer } = await server.setup({ + config$: of(configWithBasePath), + }); registerRouter(router); await server.start(); @@ -790,7 +792,7 @@ test('with defined `redirectHttpFromPort`', async () => { const router = new Router('/', logger, enhanceWithContext, routerOptions); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ body: 'value:/' })); - const { registerRouter } = await server.setup(configWithSSL); + const { registerRouter } = await server.setup({ config$: configWithSSL$ }); registerRouter(router); await server.start(); @@ -801,7 +803,7 @@ test('returns server and connection options on start', async () => { ...config, port: 12345, }; - const { server: innerServer } = await server.setup(configWithPort); + const { server: innerServer } = await server.setup({ config$: of(configWithPort) }); expect(innerServer).toBeDefined(); expect(innerServer).toBe((server as any).server); @@ -815,7 +817,7 @@ test('throws an error if starts without set up', async () => { test('allows attaching metadata to attach meta-data tag strings to a route', async () => { const tags = ['my:tag']; - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.get({ path: '/with-tags', validate: false, options: { tags } }, (context, req, res) => @@ -834,7 +836,7 @@ test('allows attaching metadata to attach meta-data tag strings to a route', asy test('allows declaring route access to flag a route as public or internal', async () => { const access = 'internal'; - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.get({ path: '/with-access', validate: false, options: { access } }, (context, req, res) => @@ -852,7 +854,7 @@ test('allows declaring route access to flag a route as public or internal', asyn }); test(`sets access flag to 'internal' if not defined`, async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.get({ path: '/internal/foo', validate: false }, (context, req, res) => @@ -883,7 +885,7 @@ test(`sets access flag to 'internal' if not defined`, async () => { }); test('exposes route details of incoming request to a route handler', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ body: req.route })); @@ -907,7 +909,9 @@ test('exposes route details of incoming request to a route handler', async () => describe('conditional compression', () => { async function setupServer(innerConfig: HttpConfig) { - const { registerRouter, server: innerServer } = await server.setup(innerConfig); + const { registerRouter, server: innerServer } = await server.setup({ + config$: of(innerConfig), + }); const router = new Router('', logger, enhanceWithContext, routerOptions); // we need the large body here so that compression would normally be used const largeRequest = { @@ -1012,8 +1016,10 @@ describe('conditional compression', () => { describe('response headers', () => { test('allows to configure "keep-alive" header', async () => { const { registerRouter, server: innerServer } = await server.setup({ - ...config, - keepaliveTimeout: 100_000, + config$: of({ + ...config, + keepaliveTimeout: 100_000, + }), }); const router = new Router('', logger, enhanceWithContext, routerOptions); @@ -1031,7 +1037,7 @@ describe('response headers', () => { }); test('default headers', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ body: req.route })); @@ -1053,7 +1059,7 @@ describe('response headers', () => { }); test('exposes route details of incoming request to a route handler (POST + payload options)', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1093,7 +1099,7 @@ test('exposes route details of incoming request to a route handler (POST + paylo describe('body options', () => { test('should reject the request because the Content-Type in the request is not valid', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1115,7 +1121,7 @@ describe('body options', () => { }); test('should reject the request because the payload is too large', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1137,7 +1143,7 @@ describe('body options', () => { }); test('should not parse the content in the request', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1166,7 +1172,7 @@ describe('body options', () => { describe('timeout options', () => { describe('payload timeout', () => { test('POST routes set the payload timeout', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1200,7 +1206,7 @@ describe('timeout options', () => { }); test('DELETE routes set the payload timeout', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.delete( @@ -1233,7 +1239,7 @@ describe('timeout options', () => { }); test('PUT routes set the payload timeout and automatically adjusts the idle socket timeout', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.put( @@ -1266,7 +1272,7 @@ describe('timeout options', () => { }); test('PATCH routes set the payload timeout and automatically adjusts the idle socket timeout', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.patch( @@ -1302,8 +1308,10 @@ describe('timeout options', () => { describe('idleSocket timeout', () => { test('uses server socket timeout when not specified in the route', async () => { const { registerRouter, server: innerServer } = await server.setup({ - ...config, - socketTimeout: 11000, + config$: of({ + ...config, + socketTimeout: 11000, + }), }); const router = new Router('', logger, enhanceWithContext, routerOptions); @@ -1335,8 +1343,10 @@ describe('timeout options', () => { test('sets the socket timeout when specified in the route', async () => { const { registerRouter, server: innerServer } = await server.setup({ - ...config, - socketTimeout: 11000, + config$: of({ + ...config, + socketTimeout: 11000, + }), }); const router = new Router('', logger, enhanceWithContext, routerOptions); @@ -1368,7 +1378,7 @@ describe('timeout options', () => { }); test('idleSocket timeout can be smaller than the payload timeout', async () => { - const { registerRouter } = await server.setup(config); + const { registerRouter } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.post( @@ -1395,7 +1405,7 @@ describe('timeout options', () => { }); test('should return a stream in the body', async () => { - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$ }); const router = new Router('', logger, enhanceWithContext, routerOptions); router.put( @@ -1421,8 +1431,10 @@ test('should return a stream in the body', async () => { test('closes sockets on timeout', async () => { const { registerRouter, server: innerServer } = await server.setup({ - ...config, - socketTimeout: 1000, + config$: of({ + ...config, + socketTimeout: 1000, + }), }); const router = new Router('', logger, enhanceWithContext, routerOptions); @@ -1446,14 +1458,14 @@ test('closes sockets on timeout', async () => { describe('setup contract', () => { describe('#createSessionStorage', () => { test('creates session storage factory', async () => { - const { createCookieSessionStorageFactory } = await server.setup(config); + const { createCookieSessionStorageFactory } = await server.setup({ config$ }); const sessionStorageFactory = await createCookieSessionStorageFactory(cookieOptions); expect(sessionStorageFactory.asScoped).toBeDefined(); }); test('creates session storage factory only once', async () => { - const { createCookieSessionStorageFactory } = await server.setup(config); + const { createCookieSessionStorageFactory } = await server.setup({ config$ }); const create = async () => await createCookieSessionStorageFactory(cookieOptions); await create(); @@ -1461,7 +1473,7 @@ describe('setup contract', () => { }); test('does not throw if called after stop', async () => { - const { createCookieSessionStorageFactory } = await server.setup(config); + const { createCookieSessionStorageFactory } = await server.setup({ config$ }); await server.stop(); expect(() => { createCookieSessionStorageFactory(cookieOptions); @@ -1471,7 +1483,7 @@ describe('setup contract', () => { describe('#getServerInfo', () => { test('returns correct information', async () => { - let { getServerInfo } = await server.setup(config); + let { getServerInfo } = await server.setup({ config$ }); expect(getServerInfo()).toEqual({ hostname: '127.0.0.1', @@ -1481,10 +1493,12 @@ describe('setup contract', () => { }); ({ getServerInfo } = await server.setup({ - ...config, - port: 12345, - name: 'custom-name', - host: 'localhost', + config$: of({ + ...config, + port: 12345, + name: 'custom-name', + host: 'localhost', + }), })); expect(getServerInfo()).toEqual({ @@ -1496,7 +1510,7 @@ describe('setup contract', () => { }); test('returns correct protocol when ssl is enabled', async () => { - const { getServerInfo } = await server.setup(configWithSSL); + const { getServerInfo } = await server.setup({ config$: configWithSSL$ }); expect(getServerInfo().protocol).toEqual('https'); }); @@ -1517,7 +1531,7 @@ describe('setup contract', () => { }); test('registers routes with expected options', async () => { - const { registerStaticDir } = await server.setup(config); + const { registerStaticDir } = await server.setup({ config$ }); expect(createServer).toHaveBeenCalledTimes(1); const [{ value: myServer }] = (createServer as jest.Mock).mock.results; jest.spyOn(myServer, 'route'); @@ -1540,7 +1554,7 @@ describe('setup contract', () => { }); test('does not throw if called after stop', async () => { - const { registerStaticDir } = await server.setup(config); + const { registerStaticDir } = await server.setup({ config$ }); await server.stop(); expect(() => { registerStaticDir('/path1/{path*}', '/path/to/resource'); @@ -1548,7 +1562,7 @@ describe('setup contract', () => { }); test('returns correct headers for static assets', async () => { - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', assetFolder); @@ -1562,7 +1576,7 @@ describe('setup contract', () => { }); test('returns compressed version if present', async () => { - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', assetFolder); @@ -1578,7 +1592,7 @@ describe('setup contract', () => { }); test('returns uncompressed version if compressed asset is not available', async () => { - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', assetFolder); @@ -1594,7 +1608,7 @@ describe('setup contract', () => { }); test('returns a 304 if etag value matches', async () => { - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', assetFolder); @@ -1613,7 +1627,7 @@ describe('setup contract', () => { }); test('serves content if etag values does not match', async () => { - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', assetFolder); @@ -1628,7 +1642,7 @@ describe('setup contract', () => { test('dynamically updates depending on the content of the file', async () => { const tempFile = join(tempDir, 'some_file.json'); - const { registerStaticDir, server: innerServer } = await server.setup(config); + const { registerStaticDir, server: innerServer } = await server.setup({ config$ }); registerStaticDir('/static/{path*}', tempDir); await server.start(); @@ -1655,7 +1669,7 @@ describe('setup contract', () => { describe('#registerOnPreRouting', () => { test('does not throw if called after stop', async () => { - const { registerOnPreRouting } = await server.setup(config); + const { registerOnPreRouting } = await server.setup({ config$ }); await server.stop(); expect(() => { registerOnPreRouting((req, res) => res.unauthorized()); @@ -1665,7 +1679,7 @@ describe('setup contract', () => { describe('#registerOnPreAuth', () => { test('does not throw if called after stop', async () => { - const { registerOnPreAuth } = await server.setup(config); + const { registerOnPreAuth } = await server.setup({ config$ }); await server.stop(); expect(() => { registerOnPreAuth((req, res) => res.unauthorized()); @@ -1675,7 +1689,7 @@ describe('setup contract', () => { describe('#registerOnPostAuth', () => { test('does not throw if called after stop', async () => { - const { registerOnPostAuth } = await server.setup(config); + const { registerOnPostAuth } = await server.setup({ config$ }); await server.stop(); expect(() => { registerOnPostAuth((req, res) => res.unauthorized()); @@ -1685,7 +1699,7 @@ describe('setup contract', () => { describe('#registerOnPreResponse', () => { test('does not throw if called after stop', async () => { - const { registerOnPreResponse } = await server.setup(config); + const { registerOnPreResponse } = await server.setup({ config$ }); await server.stop(); expect(() => { registerOnPreResponse((req, res, t) => t.next()); @@ -1695,7 +1709,7 @@ describe('setup contract', () => { describe('#registerAuth', () => { test('does not throw if called after stop', async () => { - const { registerAuth } = await server.setup(config); + const { registerAuth } = await server.setup({ config$ }); await server.stop(); expect(() => { registerAuth((req, res) => res.unauthorized()); @@ -1703,3 +1717,52 @@ describe('setup contract', () => { }); }); }); + +describe('configuration change', () => { + it('logs a warning in case of incompatible config change', async () => { + const configSubject = new BehaviorSubject(configWithSSL); + + await server.setup({ config$: configSubject }); + await server.start(); + + const nextConfig = { + ...configWithSSL, + ssl: { + ...configWithSSL.ssl, + getSecureOptions: () => 0, + enabled: false, + }, + } as HttpConfig; + + configSubject.next(nextConfig); + + expect(loggingService.get().warn).toHaveBeenCalledWith( + 'Incompatible TLS config change detected - TLS cannot be toggled without a full server reboot.' + ); + }); + + it('calls setTlsConfig and logs an info message when config changes', async () => { + const configSubject = new BehaviorSubject(configWithSSL); + + const { server: innerServer } = await server.setup({ config$: configSubject }); + await server.start(); + + const nextConfig = { + ...configWithSSL, + ssl: { + ...configWithSSL.ssl, + isEqualTo: () => false, + getSecureOptions: () => 0, + }, + } as HttpConfig; + + configSubject.next(nextConfig); + + expect(setTlsConfigMock).toHaveBeenCalledTimes(1); + expect(setTlsConfigMock).toHaveBeenCalledWith(innerServer, nextConfig.ssl); + + expect(loggingService.get().info).toHaveBeenCalledWith( + 'TLS configuration change detected - reloading TLS configuration.' + ); + }); +}); diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index fb776f5d29eb6..1361c64bb67ce 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -14,12 +14,13 @@ import { createServer, getListenerOptions, getServerOptions, + setTlsConfig, getRequestId, } from '@kbn/server-http-tools'; import type { Duration } from 'moment'; -import { firstValueFrom, Observable } from 'rxjs'; -import { take } from 'rxjs/operators'; +import { firstValueFrom, Observable, Subscription } from 'rxjs'; +import { take, pairwise } from 'rxjs/operators'; import apm from 'elastic-apm-node'; // @ts-expect-error no type definition import Brok from 'brok'; @@ -160,9 +161,15 @@ export type LifecycleRegistrar = Pick< | 'registerOnPreResponse' >; +export interface HttpServerSetupOptions { + config$: Observable; + executionContext?: InternalExecutionContextSetup; +} + export class HttpServer { private server?: Server; private config?: HttpConfig; + private subscriptions: Subscription[] = []; private registeredRouters = new Set(); private authRegistered = false; private cookieSessionStorageCreated = false; @@ -209,13 +216,16 @@ export class HttpServer { } } - public async setup( - config: HttpConfig, - executionContext?: InternalExecutionContextSetup - ): Promise { + public async setup({ + config$, + executionContext, + }: HttpServerSetupOptions): Promise { + const config = await firstValueFrom(config$); + this.config = config; + const serverOptions = getServerOptions(config); const listenerOptions = getListenerOptions(config); - this.config = config; + this.server = createServer(serverOptions, listenerOptions); await this.server.register([HapiStaticFiles]); if (config.compression.brotli.enabled) { @@ -227,6 +237,29 @@ export class HttpServer { }); } + // only hot-reloading TLS config - don't need to subscribe if TLS is initially disabled, + // given we can't hot-switch from/to enabled/disabled. + if (config.ssl.enabled) { + const configSubscription = config$ + .pipe(pairwise()) + .subscribe(([{ ssl: prevSslConfig }, { ssl: newSslConfig }]) => { + if (prevSslConfig.enabled !== newSslConfig.enabled) { + this.log.warn( + 'Incompatible TLS config change detected - TLS cannot be toggled without a full server reboot.' + ); + return; + } + + const sameConfig = newSslConfig.isEqualTo(prevSslConfig); + + if (!sameConfig) { + this.log.info('TLS configuration change detected - reloading TLS configuration.'); + setTlsConfig(this.server!, newSslConfig); + } + }); + this.subscriptions.push(configSubscription); + } + // It's important to have setupRequestStateAssignment call the very first, otherwise context passing will be broken. // That's the only reason why context initialization exists in this method. this.setupRequestStateAssignment(config, executionContext); diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 99d286b26f83f..b09e7e84fef21 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -71,7 +71,7 @@ export class HttpService this.env = env; this.log = logger.get('http'); this.config$ = combineLatest([ - configService.atPath(httpConfig.path), + configService.atPath(httpConfig.path, { ignoreUnchanged: false }), configService.atPath(cspConfig.path), configService.atPath(externalUrlConfig.path), ]).pipe(map(([http, csp, externalUrl]) => new HttpConfig(http, csp, externalUrl))); @@ -85,7 +85,9 @@ export class HttpService this.log.debug('setting up preboot server'); const config = await firstValueFrom(this.config$); - const prebootSetup = await this.prebootServer.setup(config); + const prebootSetup = await this.prebootServer.setup({ + config$: this.config$, + }); prebootSetup.server.route({ path: '/{p*}', method: '*', @@ -157,10 +159,10 @@ export class HttpService const config = await firstValueFrom(this.config$); - const { registerRouter, ...serverContract } = await this.httpServer.setup( - config, - deps.executionContext - ); + const { registerRouter, ...serverContract } = await this.httpServer.setup({ + config$: this.config$, + executionContext: deps.executionContext, + }); registerCoreHandlers(serverContract, config, this.env, this.log); diff --git a/packages/core/logging/core-logging-server-internal/src/__snapshots__/logging_system.test.ts.snap b/packages/core/logging/core-logging-server-internal/src/__snapshots__/logging_system.test.ts.snap index 01ffd79b0b87f..d283fc1400c78 100644 --- a/packages/core/logging/core-logging-server-internal/src/__snapshots__/logging_system.test.ts.snap +++ b/packages/core/logging/core-logging-server-internal/src/__snapshots__/logging_system.test.ts.snap @@ -25,6 +25,7 @@ Object { "message": "buffered trace message", "process": Object { "pid": Any, + "uptime": 10, }, } `; @@ -42,6 +43,7 @@ Object { "message": "buffered info message", "process": Object { "pid": Any, + "uptime": 10, }, "some": "value", } @@ -60,6 +62,7 @@ Object { "message": "buffered fatal message", "process": Object { "pid": Any, + "uptime": 10, }, } `; @@ -77,6 +80,7 @@ Object { "message": "buffered info message", "process": Object { "pid": Any, + "uptime": 10, }, "some": "value", } @@ -95,6 +99,7 @@ Object { "message": "some new info message", "process": Object { "pid": Any, + "uptime": 10, }, } `; diff --git a/packages/core/logging/core-logging-server-internal/src/layouts/__snapshots__/json_layout.test.ts.snap b/packages/core/logging/core-logging-server-internal/src/layouts/__snapshots__/json_layout.test.ts.snap index 0809dbffce670..da1c1b4c4f8b4 100644 --- a/packages/core/logging/core-logging-server-internal/src/layouts/__snapshots__/json_layout.test.ts.snap +++ b/packages/core/logging/core-logging-server-internal/src/layouts/__snapshots__/json_layout.test.ts.snap @@ -15,6 +15,7 @@ Object { "message": "message-1", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -29,6 +30,7 @@ Object { "message": "message-2", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -43,6 +45,7 @@ Object { "message": "message-3", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -57,6 +60,7 @@ Object { "message": "message-4", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -71,6 +75,7 @@ Object { "message": "message-5", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -85,6 +90,7 @@ Object { "message": "message-6", "process": Object { "pid": 5355, + "uptime": 10, }, } `; @@ -99,6 +105,7 @@ Object { "message": "message-6", "process": Object { "pid": 5355, + "uptime": 10, }, "span": Object { "id": "spanId-1", diff --git a/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.test.ts b/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.test.ts index 60b415b1b2a2b..8d16ce8a576f2 100644 --- a/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.test.ts +++ b/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.test.ts @@ -10,6 +10,8 @@ import { EcsVersion } from '@kbn/ecs'; import { LogLevel, LogRecord } from '@kbn/logging'; import { JsonLayout } from './json_layout'; +jest.spyOn(process, 'uptime').mockReturnValue(10); + const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 30, 22, 11)); const records: LogRecord[] = [ { @@ -121,6 +123,7 @@ test('`format()` correctly formats record with meta-data', () => { }, process: { pid: 5355, + uptime: 10, }, }); }); @@ -169,6 +172,7 @@ test('`format()` correctly formats error record with meta-data', () => { }, process: { pid: 5355, + uptime: 10, }, }); }); @@ -202,6 +206,7 @@ test('format() meta can merge override logs', () => { }, process: { pid: 3, + uptime: 10, }, }); }); @@ -232,6 +237,7 @@ test('format() meta can not override message', () => { }, process: { pid: 3, + uptime: 10, }, }); }); @@ -262,6 +268,7 @@ test('format() meta can not override ecs version', () => { }, process: { pid: 3, + uptime: 10, }, }); }); @@ -295,6 +302,7 @@ test('format() meta can not override logger or level', () => { }, process: { pid: 3, + uptime: 10, }, }); }); @@ -325,6 +333,7 @@ test('format() meta can not override timestamp', () => { }, process: { pid: 3, + uptime: 10, }, }); }); @@ -359,6 +368,7 @@ test('format() meta can not override tracing properties', () => { }, process: { pid: 3, + uptime: 10, }, span: { id: 'span_override' }, trace: { id: 'trace_override' }, @@ -404,6 +414,7 @@ test('format() meta.toJSON() is used if own property', () => { }, process: { pid: 3, + uptime: 10, }, server: { address: 'localhost', @@ -448,6 +459,7 @@ test('format() meta.toJSON() is used if present on prototype', () => { }, process: { pid: 3, + uptime: 10, }, foo: 'bar', }); diff --git a/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.ts b/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.ts index 43b906fa8407c..59921b0f4b7f5 100644 --- a/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.ts +++ b/packages/core/logging/core-logging-server-internal/src/layouts/json_layout.ts @@ -53,6 +53,7 @@ export class JsonLayout implements Layout { }, process: { pid: record.pid, + uptime: process.uptime(), }, span: spanId ? { id: spanId } : undefined, trace: traceId ? { id: traceId } : undefined, diff --git a/packages/core/logging/core-logging-server-internal/src/logging_system.test.ts b/packages/core/logging/core-logging-server-internal/src/logging_system.test.ts index 81eb539a618fc..76fe93d1e614a 100644 --- a/packages/core/logging/core-logging-server-internal/src/logging_system.test.ts +++ b/packages/core/logging/core-logging-server-internal/src/logging_system.test.ts @@ -23,6 +23,7 @@ let system: LoggingSystem; beforeEach(() => { mockConsoleLog = jest.spyOn(global.console, 'log').mockReturnValue(undefined); jest.spyOn(global, 'Date').mockImplementation(() => timestamp); + jest.spyOn(process, 'uptime').mockReturnValue(10); system = new LoggingSystem(); }); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.test.ts index 4ac8870de5051..ee85fd9b755bb 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.test.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.test.ts @@ -93,10 +93,7 @@ describe('OsCgroupMetricsCollector', () => { await collector.collect(); expect(gatherV2CgroupMetrics).toHaveBeenCalledTimes(1); - expect(gatherV2CgroupMetrics).toHaveBeenCalledWith({ - cpuAcctPath: '/groupname', - cpuPath: '/groupname', - }); + expect(gatherV2CgroupMetrics).toHaveBeenCalledWith('/groupname'); expect(gatherV1CgroupMetrics).toHaveBeenCalledTimes(0); }); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.ts index b336bff923b25..cea83674faa9c 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/cgroup.ts @@ -42,9 +42,13 @@ export class OsCgroupMetricsCollector implements MetricsCollector { if (this.hasPaths()) return; - const { data: cgroups, v2 } = await gatherInfo(); - this.isCgroup2 = v2; - this.cpuPath = this.options.cpuPath || cgroups[GROUP_CPU]; - this.cpuAcctPath = this.options.cpuAcctPath || cgroups[GROUP_CPUACCT]; + const result = await gatherInfo(); + this.isCgroup2 = result.v2; + if (result.v2) { + this.cpuPath = result.path; + this.cpuAcctPath = result.path; + } else { + this.cpuPath = this.options.cpuPath || result.data[GROUP_CPU]; + this.cpuAcctPath = this.options.cpuAcctPath || result.data[GROUP_CPUACCT]; + } // prevents undefined cgroup paths this.noCgroupPresent = Boolean(!this.cpuPath || !this.cpuAcctPath); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.test.ts index bf86907e656d9..8324091eaced7 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.test.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.test.ts @@ -17,11 +17,14 @@ describe('gatherInfo', () => { '/proc/self/cgroup': `0:controller:/path 1:controller2,controller3:/otherpath`, }); - const { data } = await gatherInfo(); - expect(data).toEqual({ - controller: '/path', - controller2: '/otherpath', - controller3: '/otherpath', + const result = await gatherInfo(); + expect(result).toEqual({ + v2: false, + data: { + controller: '/path', + controller2: '/otherpath', + controller3: '/otherpath', + }, }); }); @@ -30,7 +33,7 @@ describe('gatherInfo', () => { '/proc/self/cgroup': `0:controller:/path 1:controller2,controller3:/otherpath`, }); - await expect(gatherInfo()).resolves.toMatchObject({ v2: false }); + expect(await gatherInfo()).toMatchObject({ v2: false }); mockFs({ '/proc/self/cgroup': ` @@ -38,7 +41,7 @@ describe('gatherInfo', () => { `, }); - await expect(gatherInfo()).resolves.toMatchObject({ v2: true }); + expect(await gatherInfo()).toMatchObject({ v2: true }); }); test('missing cgroup file', async () => { diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.ts index 7e490a577ff07..0afbceb85d960 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/gather_info.ts @@ -7,8 +7,6 @@ */ import fs from 'fs/promises'; -import { GROUP_CPU, GROUP_CPUACCT } from './constants'; - const CONTROL_GROUP_RE = new RegExp('\\d+:([^:]+):(/.*)'); const CONTROLLER_SEPARATOR_RE = ','; const PROC_SELF_CGROUP_FILE = '/proc/self/cgroup'; @@ -27,10 +25,15 @@ async function readProcSelf(): Promise { return data.split(/\n/).filter((line) => line.trim().length > 0); } -interface Result { - data: Record; - v2: boolean; -} +type Result = + | { + v2: true; + path: string; + } + | { + v2: false; + data: Record; + }; export async function gatherInfo(): Promise { const lines = await readProcSelf(); @@ -39,11 +42,8 @@ export async function gatherInfo(): Promise { // eslint-disable-next-line prettier/prettier const [/* '0' */, /* '' */, path] = lines[0].trim().split(':'); return { - data: { - [GROUP_CPU]: path, - [GROUP_CPUACCT]: path, - }, v2: true, + path, }; } diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/types.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/types.ts index 84b03e3ba151a..133de91efb37f 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/types.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/types.ts @@ -8,4 +8,4 @@ import type { OpsOsMetrics } from '@kbn/core-metrics-server'; -export type OsCgroupMetrics = Pick; +export type OsCgroupMetrics = Pick; diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.test.ts index 96c720fe9a639..17f781a401ece 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.test.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.test.ts @@ -22,10 +22,17 @@ system_usec 125968 nr_periods 123 nr_throttled 1 throttled_usec 123123`, + '/sys/fs/cgroup/memory.current': '9000', + '/sys/fs/cgroup/memory.swap.current': '42', }); - expect(await gatherV2CgroupMetrics({ cpuAcctPath: '/', cpuPath: '/' })).toMatchInlineSnapshot(` + const metrics = await gatherV2CgroupMetrics('/'); + expect(metrics).toMatchInlineSnapshot(` Object { + "cgroup_memory": Object { + "current_in_bytes": 9000, + "swap_current_in_bytes": 42, + }, "cpu": Object { "cfs_period_micros": 100000, "cfs_quota_micros": -1, @@ -54,11 +61,17 @@ system_usec 125968 nr_periods 123 nr_throttled 1 throttled_usec 123123`, + '/sys/fs/cgroup/mypath/memory.current': '9876', + '/sys/fs/cgroup/mypath/memory.swap.current': '132645', }); - expect(await gatherV2CgroupMetrics({ cpuAcctPath: '/mypath', cpuPath: '/mypath' })) - .toMatchInlineSnapshot(` + const metrics = await gatherV2CgroupMetrics('/mypath'); + expect(metrics).toMatchInlineSnapshot(` Object { + "cgroup_memory": Object { + "current_in_bytes": 9876, + "swap_current_in_bytes": 132645, + }, "cpu": Object { "cfs_period_micros": 100000, "cfs_quota_micros": 111, diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.ts index ac7fba7cfaf4d..3d5d1f563d28f 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/cgroup/v2.ts @@ -13,37 +13,60 @@ import type { OsCgroupMetrics } from './types'; const PROC_CGROUP2_DIR = '/sys/fs/cgroup'; const CPU_STATS_FILE = 'cpu.stat'; const CPU_MAX_FILE = 'cpu.max'; +const MEMORY_CURRENT_FILE = 'memory.current'; +const MEMORY_SWAP_CURRENT_FILE = 'memory.swap.current'; -interface Arg { - cpuPath: string; - cpuAcctPath: string; -} +const getCGroupFilePath = (group: string, fileName: string): string => { + return joinPath(PROC_CGROUP2_DIR, group, fileName); +}; -export async function gatherV2CgroupMetrics(arg: Arg): Promise { - const [{ usage_nanos: usageNanos, ...stat }, cpuMax] = await Promise.all([ - readCPUStat(arg.cpuPath), - readCPUMax(arg.cpuPath), - ]); +export async function gatherV2CgroupMetrics(group: string): Promise { + const [{ usage_nanos: usageNanos, ...stat }, cpuMax, memoryCurrent, swapCurrent] = + await Promise.all([ + readCPUStat(group), + readCPUMax(group), + readMemoryCurrent(group), + readSwapCurrent(group), + ]); return { cpu: { ...cpuMax, - control_group: arg.cpuPath, + control_group: group, stat, }, cpuacct: { - control_group: arg.cpuPath, + control_group: group, usage_nanos: usageNanos, }, + cgroup_memory: { + current_in_bytes: memoryCurrent, + swap_current_in_bytes: swapCurrent, + }, }; } + interface CPUMax { cfs_period_micros: number; cfs_quota_micros: number; } +async function readMemoryCurrent(group: string): Promise { + const rawMemoryCurrent = (await fs.readFile(getCGroupFilePath(group, MEMORY_CURRENT_FILE))) + .toString() + .trim(); + return parseInt(rawMemoryCurrent, 10); +} + +async function readSwapCurrent(group: string): Promise { + const rawMemoryCurrent = (await fs.readFile(getCGroupFilePath(group, MEMORY_SWAP_CURRENT_FILE))) + .toString() + .trim(); + return parseInt(rawMemoryCurrent, 10); +} + async function readCPUMax(group: string): Promise { - const [quota, period] = (await fs.readFile(joinPath(PROC_CGROUP2_DIR, group, CPU_MAX_FILE))) + const [quota, period] = (await fs.readFile(getCGroupFilePath(group, CPU_MAX_FILE))) .toString() .trim() .split(/\s+/); @@ -62,7 +85,7 @@ async function readCPUStat(group: string): Promise { time_throttled_nanos: -1, usage_nanos: -1, }; - return (await fs.readFile(joinPath(PROC_CGROUP2_DIR, group, CPU_STATS_FILE))) + return (await fs.readFile(getCGroupFilePath(group, CPU_STATS_FILE))) .toString() .split(/\n/) .reduce((acc, line) => { diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/mocks_internal.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/mocks_internal.ts index 812b2c89ff8d4..d8659d702d0c8 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/mocks_internal.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/mocks_internal.ts @@ -37,6 +37,8 @@ function createMockOpsProcessMetrics(): OpsProcessMetrics { memory: { heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 }, resident_set_size_in_bytes: 1, + array_buffers_in_bytes: 1, + external_in_bytes: 1, }, event_loop_delay: 1, event_loop_delay_histogram: histogram, diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/process.test.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/process.test.ts index dbb667aeeb043..4bad8e24b322e 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/process.test.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/process.test.ts @@ -61,12 +61,15 @@ describe('ProcessMetricsCollector', () => { const heapUsed = 4688; const heapSizeLimit = 5788; const rss = 5865; + const external = 9001; + const arrayBuffers = 42; + jest.spyOn(process, 'memoryUsage').mockImplementation(() => ({ rss, heapTotal, heapUsed, - external: 0, - arrayBuffers: 0, + external, + arrayBuffers, })); jest.spyOn(v8, 'getHeapStatistics').mockImplementation( @@ -83,6 +86,8 @@ describe('ProcessMetricsCollector', () => { expect(metrics[0].memory.heap.used_in_bytes).toEqual(heapUsed); expect(metrics[0].memory.heap.size_limit).toEqual(heapSizeLimit); expect(metrics[0].memory.resident_set_size_in_bytes).toEqual(rss); + expect(metrics[0].memory.external_in_bytes).toEqual(external); + expect(metrics[0].memory.array_buffers_in_bytes).toEqual(arrayBuffers); }); }); diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/process.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/process.ts index 399da1ad9c29e..3e35eaa108ea9 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/process.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/process.ts @@ -38,6 +38,8 @@ export class ProcessMetricsCollector implements MetricsCollector): OpsMetrics { ...testMetrics, }; } + const testMetrics = { process: { - memory: { heap: { used_in_bytes: 100 } }, + memory: { + heap: { used_in_bytes: 100, total_in_bytes: 200, size_limit: 300 }, + resident_set_size_in_bytes: 400, + external_in_bytes: 500, + array_buffers_in_bytes: 600, + }, uptime_in_millis: 1500, event_loop_delay: 50, event_loop_delay_histogram: { percentiles: { '50': 50, '75': 75, '95': 95, '99': 99 } }, @@ -127,9 +133,14 @@ describe('getEcsOpsMetricsLog', () => { "utilization": 0.6365329598160299, }, "memory": Object { + "arrayBuffersInBytes": 600, + "externalInBytes": 500, "heap": Object { + "sizeLimit": 300, + "totalInBytes": 200, "usedInBytes": 100, }, + "residentSetSizeInBytes": 400, }, "uptime": 1, }, diff --git a/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts b/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts index 78e4482b7a351..e3fffbdeb0433 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/logging/get_ops_metrics_log.ts @@ -86,7 +86,12 @@ export function getEcsOpsMetricsLog(metrics: OpsMetrics) { memory: { heap: { usedInBytes: processMemoryUsedInBytes, + totalInBytes: process?.memory?.heap.total_in_bytes, + sizeLimit: process?.memory?.heap.size_limit, }, + residentSetSizeInBytes: process?.memory?.resident_set_size_in_bytes, + externalInBytes: process?.memory?.external_in_bytes, + arrayBuffersInBytes: process?.memory?.array_buffers_in_bytes, }, eventLoopDelay: eventLoopDelayVal, eventLoopDelayHistogram: eventLoopDelayHistVals, diff --git a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts index 45b3ae49a1f5e..176ad9c7fa4ce 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/metrics_service.test.ts @@ -216,9 +216,14 @@ describe('MetricsService', () => { "eventLoopDelayHistogram": undefined, "eventLoopUtilization": undefined, "memory": Object { + "arrayBuffersInBytes": undefined, + "externalInBytes": undefined, "heap": Object { + "sizeLimit": undefined, + "totalInBytes": undefined, "usedInBytes": undefined, }, + "residentSetSizeInBytes": undefined, }, "uptime": undefined, }, diff --git a/packages/core/metrics/core-metrics-server/src/metrics.ts b/packages/core/metrics/core-metrics-server/src/metrics.ts index 6f903cd66a246..615998e600a29 100644 --- a/packages/core/metrics/core-metrics-server/src/metrics.ts +++ b/packages/core/metrics/core-metrics-server/src/metrics.ts @@ -82,6 +82,10 @@ export interface OpsProcessMetrics { }; /** node rss */ resident_set_size_in_bytes: number; + /** memory usage of C++ objects bound to JavaScript objects managed by V8 */ + external_in_bytes: number; + /** memory allocated for array buffers. This is also included in the external value*/ + array_buffers_in_bytes: number; }; /** mean event loop delay since last collection*/ event_loop_delay: number; @@ -153,6 +157,14 @@ export interface OpsOsMetrics { time_throttled_nanos: number; }; }; + + /** memory cgroup metrics, undefined when not running in cgroup v2 */ + cgroup_memory?: { + /** The total amount of memory currently being used by the cgroup and its descendants. */ + current_in_bytes: number; + /** The total amount of swap currently being used by the cgroup and its descendants. */ + swap_current_in_bytes: number; + }; } /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts index d24c11f190696..60deaa64e3e63 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts @@ -75,6 +75,17 @@ describe('SavedObjectsRepository', () => { return expect.toBeDocumentWithoutError(type, id); }; + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + beforeEach(() => { pointInTimeFinderMock.mockClear(); client = elasticsearchClientMock.createElasticsearchClient(); @@ -121,7 +132,7 @@ describe('SavedObjectsRepository', () => { const originId = 'some-origin-id'; const namespace = 'foo-namespace'; - // bulk create calls have two objects for each source -- the action, and the source + // bulk index calls have two objects for each source -- the action, and the source const expectClientCallArgsAction = ( objects: TypeIdTuple[], { @@ -153,14 +164,26 @@ describe('SavedObjectsRepository', () => { ); }; - const expectObjArgs = ({ type, attributes }: { type: string; attributes: unknown }) => [ - expect.any(Object), + const expectObjArgs = ( { - doc: expect.objectContaining({ - [type]: attributes, - ...mockTimestampFields, - }), + type, + attributes, + references, + }: { + type: string; + attributes: unknown; + references?: SavedObjectReference[]; }, + overrides: Record = {} + ) => [ + expect.any(Object), + expect.objectContaining({ + [type]: attributes, + references, + type, + ...overrides, + ...mockTimestampFields, + }), ]; describe('client calls', () => { @@ -169,13 +192,14 @@ describe('SavedObjectsRepository', () => { expect(client.bulk).toHaveBeenCalled(); }); - it(`should use the ES mget action before bulk action for any types that are multi-namespace`, async () => { + it(`should use the ES mget action before bulk action for any types that are valid`, async () => { const objects = [obj1, { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }]; await bulkUpdateSuccess(client, repository, registry, objects); expect(client.bulk).toHaveBeenCalled(); expect(client.mget).toHaveBeenCalled(); const docs = [ + expect.objectContaining({ _id: `${obj1.type}:${obj1.id}` }), expect.objectContaining({ _id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${obj2.id}` }), ]; expect(client.mget).toHaveBeenCalledWith( @@ -186,21 +210,14 @@ describe('SavedObjectsRepository', () => { it(`formats the ES request`, async () => { await bulkUpdateSuccess(client, repository, registry, [obj1, obj2]); - const body = [...expectObjArgs(obj1), ...expectObjArgs(obj2)]; - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); + // expect client.bulk call args should include the whole doc + expectClientCallArgsAction([obj1, obj2], { method: 'index' }); }); it(`formats the ES request for any types that are multi-namespace`, async () => { const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; await bulkUpdateSuccess(client, repository, registry, [obj1, _obj2]); - const body = [...expectObjArgs(obj1), ...expectObjArgs(_obj2)]; - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); + expectClientCallArgsAction([obj1, _obj2], { method: 'index' }); }); it(`doesnt call Elasticsearch if there are no valid objects to update`, async () => { @@ -211,8 +228,10 @@ describe('SavedObjectsRepository', () => { it(`defaults to no references`, async () => { await bulkUpdateSuccess(client, repository, registry, [obj1, obj2]); - const expected = { doc: expect.not.objectContaining({ references: expect.anything() }) }; - const body = [expect.any(Object), expected, expect.any(Object), expected]; + const body = [ + ...expectObjArgs({ ...obj1, references: [] }), + ...expectObjArgs({ ...obj2, references: [] }), + ]; expect(client.bulk).toHaveBeenCalledWith( expect.objectContaining({ body }), expect.anything() @@ -223,13 +242,16 @@ describe('SavedObjectsRepository', () => { const test = async (references: SavedObjectReference[]) => { const objects = [obj1, obj2].map((obj) => ({ ...obj, references })); await bulkUpdateSuccess(client, repository, registry, objects); - const expected = { doc: expect.objectContaining({ references }) }; - const body = [expect.any(Object), expected, expect.any(Object), expected]; + const body = [ + ...expectObjArgs({ ...obj1, references }), + ...expectObjArgs({ ...obj2, references }), + ]; expect(client.bulk).toHaveBeenCalledWith( expect.objectContaining({ body }), expect.anything() ); client.bulk.mockClear(); + client.mget.mockClear(); }; await test(references); await test([{ type: 'type', id: 'id', name: 'some ref' }]); @@ -238,15 +260,18 @@ describe('SavedObjectsRepository', () => { it(`doesn't accept custom references if not an array`, async () => { const test = async (references: unknown) => { - const objects = [obj1, obj2]; // .map((obj) => ({ ...obj })); + const objects = [obj1, obj2]; await bulkUpdateSuccess(client, repository, registry, objects); - const expected = { doc: expect.not.objectContaining({ references: expect.anything() }) }; - const body = [expect.any(Object), expected, expect.any(Object), expected]; + const body = [ + ...expectObjArgs({ ...obj1, references: expect.not.arrayContaining([references]) }), + ...expectObjArgs({ ...obj2, references: expect.not.arrayContaining([references]) }), + ]; expect(client.bulk).toHaveBeenCalledWith( expect.objectContaining({ body }), expect.anything() ); client.bulk.mockClear(); + client.mget.mockClear(); }; await test('string'); await test(123); @@ -265,7 +290,7 @@ describe('SavedObjectsRepository', () => { it(`defaults to no version for types that are not multi-namespace`, async () => { const objects = [obj1, { ...obj2, type: NAMESPACE_AGNOSTIC_TYPE }]; await bulkUpdateSuccess(client, repository, registry, objects); - expectClientCallArgsAction(objects, { method: 'update' }); + expectClientCallArgsAction(objects, { method: 'index' }); }); it(`accepts version`, async () => { @@ -277,13 +302,13 @@ describe('SavedObjectsRepository', () => { ]; await bulkUpdateSuccess(client, repository, registry, objects); const overrides = { if_seq_no: 100, if_primary_term: 200 }; - expectClientCallArgsAction(objects, { method: 'update', overrides }); + expectClientCallArgsAction(objects, { method: 'index', overrides }); }); it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) await bulkUpdateSuccess(client, repository, registry, [obj1, obj2], { namespace }); - expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + expectClientCallArgsAction([obj1, obj2], { method: 'index', getId }); jest.clearAllMocks(); // test again with object namespace string that supersedes the operation's namespace ID @@ -291,13 +316,13 @@ describe('SavedObjectsRepository', () => { { ...obj1, namespace }, { ...obj2, namespace }, ]); - expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + expectClientCallArgsAction([obj1, obj2], { method: 'index', getId }); }); it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) await bulkUpdateSuccess(client, repository, registry, [obj1, obj2]); - expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + expectClientCallArgsAction([obj1, obj2], { method: 'index', getId }); jest.clearAllMocks(); // test again with object namespace string that supersedes the operation's namespace ID @@ -311,7 +336,7 @@ describe('SavedObjectsRepository', () => { ], { namespace } ); - expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + expectClientCallArgsAction([obj1, obj2], { method: 'index', getId }); }); it(`normalizes options.namespace from 'default' to undefined`, async () => { @@ -319,7 +344,7 @@ describe('SavedObjectsRepository', () => { await bulkUpdateSuccess(client, repository, registry, [obj1, obj2], { namespace: 'default', }); - expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + expectClientCallArgsAction([obj1, obj2], { method: 'index', getId }); }); it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { @@ -328,18 +353,20 @@ describe('SavedObjectsRepository', () => { const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; await bulkUpdateSuccess(client, repository, registry, [_obj1], { namespace }); - expectClientCallArgsAction([_obj1], { method: 'update', getId }); + expectClientCallArgsAction([_obj1], { method: 'index', getId }); client.bulk.mockClear(); + client.mget.mockClear(); await bulkUpdateSuccess(client, repository, registry, [_obj2], { namespace }); - expectClientCallArgsAction([_obj2], { method: 'update', getId }); + expectClientCallArgsAction([_obj2], { method: 'index', getId }); jest.clearAllMocks(); // test again with object namespace string that supersedes the operation's namespace ID await bulkUpdateSuccess(client, repository, registry, [{ ..._obj1, namespace }]); - expectClientCallArgsAction([_obj1], { method: 'update', getId }); + expectClientCallArgsAction([_obj1], { method: 'index', getId }); client.bulk.mockClear(); + client.mget.mockClear(); await bulkUpdateSuccess(client, repository, registry, [{ ..._obj2, namespace }]); - expectClientCallArgsAction([_obj2], { method: 'update', getId }); + expectClientCallArgsAction([_obj2], { method: 'index', getId }); }); }); @@ -359,51 +386,71 @@ describe('SavedObjectsRepository', () => { isBulkError: boolean, expectedErrorResult: ExpectedErrorResult ) => { - const objects = [obj1, obj, obj2]; - const mockResponse = getMockBulkUpdateResponse(registry, objects); + const objects = [obj1, obj2, obj]; + + const mockedMgetResponse = getMockMgetResponse(registry, [obj1, obj2, obj]); + client.bulk.mockClear(); + client.mget.mockClear(); + client.mget.mockResponseOnce(mockedMgetResponse); + + const mockBulkIndexResponse = getMockBulkUpdateResponse(registry, objects); if (isBulkError) { - // mock the bulk error for only the second object + // mock the bulk error for only the third object + mockGetBulkOperationError.mockReturnValueOnce(undefined); mockGetBulkOperationError.mockReturnValueOnce(undefined); mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error as Payload); } - client.bulk.mockResponseOnce(mockResponse); + client.bulk.mockResponseOnce(mockBulkIndexResponse); const result = await repository.bulkUpdate(objects); + + expect(client.mget).toHaveBeenCalled(); expect(client.bulk).toHaveBeenCalled(); - const objCall = isBulkError ? expectObjArgs(obj) : []; - const body = [...expectObjArgs(obj1), ...objCall, ...expectObjArgs(obj2)]; - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); + + const expectClientCallObjects = isBulkError ? [obj1, obj2, obj] : [obj1, obj2]; + expectClientCallArgsAction(expectClientCallObjects, { method: 'index' }); + expect(result).toEqual({ - saved_objects: [expectSuccess(obj1), expectedErrorResult, expectSuccess(obj2)], + saved_objects: [expectSuccess(obj1), expectSuccess(obj2), expectedErrorResult], }); }; const bulkUpdateMultiError = async ( - [obj1, _obj, obj2]: SavedObjectsBulkUpdateObject[], + [obj1, obj2, _obj]: SavedObjectsBulkUpdateObject[], options: SavedObjectsBulkUpdateOptions | undefined, mgetResponse: estypes.MgetResponse, mgetOptions?: { statusCode?: number } ) => { + client.bulk.mockClear(); + client.mget.mockClear(); + // we only need to mock the response once. A 404 status code will apply to the response for all client.mget.mockResponseOnce(mgetResponse, { statusCode: mgetOptions?.statusCode }); - const bulkResponse = getMockBulkUpdateResponse(registry, [obj1, obj2], { namespace }); - client.bulk.mockResponseOnce(bulkResponse); + const mockBulkIndexResponse = getMockBulkUpdateResponse(registry, [obj1, obj2], { + namespace, + }); + client.bulk.mockResponseOnce(mockBulkIndexResponse); + + const result = await repository.bulkUpdate([obj1, obj2, _obj], options); - const result = await repository.bulkUpdate([obj1, _obj, obj2], options); - expect(client.bulk).toHaveBeenCalled(); expect(client.mget).toHaveBeenCalled(); - const body = [...expectObjArgs(obj1), ...expectObjArgs(obj2)]; - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); - - expect(result).toEqual({ - saved_objects: [expectSuccess(obj1), expectErrorNotFound(_obj), expectSuccess(obj2)], - }); + if (mgetOptions?.statusCode === 404) { + expect(client.bulk).not.toHaveBeenCalled(); + expect(result).toEqual({ + saved_objects: [ + expectErrorNotFound(obj1), + expectErrorNotFound(obj2), + expectErrorNotFound(_obj), + ], + }); + } else { + expect(client.bulk).toHaveBeenCalled(); + expectClientCallArgsAction([obj1, obj2], { method: 'index' }); + + expect(result).toEqual({ + saved_objects: [expectSuccess(obj1), expectSuccess(obj2), expectErrorNotFound(_obj)], + }); + } }; it(`throws when options.namespace is '*'`, async () => { @@ -433,22 +480,22 @@ describe('SavedObjectsRepository', () => { it(`returns error when ES is unable to find the document (mget)`, async () => { const _obj = { ...obj, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; - const mgetResponse = getMockMgetResponse(registry, [_obj]); - await bulkUpdateMultiError([obj1, _obj, obj2], undefined, mgetResponse); + const mgetResponse = getMockMgetResponse(registry, [obj1, obj2, _obj]); + await bulkUpdateMultiError([obj1, obj2, _obj], undefined, mgetResponse); }); it(`returns error when ES is unable to find the index (mget)`, async () => { const _obj = { ...obj, type: MULTI_NAMESPACE_ISOLATED_TYPE }; - const mgetResponse = getMockMgetResponse(registry, [_obj]); - await bulkUpdateMultiError([obj1, _obj, obj2], { namespace }, mgetResponse, { + const mgetResponse = getMockMgetResponse(registry, [obj1, obj2, _obj]); + await bulkUpdateMultiError([obj1, obj2, _obj], { namespace }, mgetResponse, { statusCode: 404, }); }); it(`returns error when there is a conflict with an existing multi-namespace saved object (mget)`, async () => { const _obj = { ...obj, type: MULTI_NAMESPACE_ISOLATED_TYPE }; - const mgetResponse = getMockMgetResponse(registry, [_obj], 'bar-namespace'); - await bulkUpdateMultiError([obj1, _obj, obj2], { namespace }, mgetResponse); + const mgetResponse = getMockMgetResponse(registry, [obj1, obj2, _obj], 'bar-namespace'); + await bulkUpdateMultiError([obj1, obj2, _obj], { namespace }, mgetResponse); }); it(`returns bulk error`, async () => { @@ -460,6 +507,52 @@ describe('SavedObjectsRepository', () => { await bulkUpdateError(obj, true, expectedErrorResult); }); }); + describe('migration', () => { + it('migrates the fetched documents from Mget', async () => { + const modifiedObj2 = { ...obj2, coreMigrationVersion: '8.0.0' }; + const objects = [modifiedObj2]; + migrator.migrateDocument.mockImplementationOnce((doc) => ({ ...doc, migrated: true })); + + await bulkUpdateSuccess(client, repository, registry, objects); + expect(migrator.migrateDocument).toHaveBeenCalledTimes(2); + expectMigrationArgs({ + id: modifiedObj2.id, + type: modifiedObj2.type, + }); + }); + + it('migrates namespace agnostic and multinamespace object documents', async () => { + const modifiedObj2 = { + ...obj2, + coreMigrationVersion: '8.0.0', + type: MULTI_NAMESPACE_ISOLATED_TYPE, + namespace: 'default', + }; + const modifiedObj1 = { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }; + const objects = [modifiedObj2, modifiedObj1]; + migrator.migrateDocument.mockImplementationOnce((doc) => ({ ...doc, migrated: true })); + + await bulkUpdateSuccess(client, repository, registry, objects, { namespace }); + + expect(migrator.migrateDocument).toHaveBeenCalledTimes(4); + expectMigrationArgs( + { + id: modifiedObj2.id, + type: modifiedObj2.type, + }, + true, + 1 + ); + expectMigrationArgs( + { + id: modifiedObj1.id, + type: modifiedObj1.type, + }, + true, + 2 + ); + }); + }); describe('returns', () => { it(`formats the ES response`, async () => { @@ -483,14 +576,24 @@ describe('SavedObjectsRepository', () => { id: 'three', attributes: {}, }; - const objects = [obj1, obj, obj2]; - const mockResponse = getMockBulkUpdateResponse(registry, objects); - client.bulk.mockResponseOnce(mockResponse); + const objects = [obj1, obj2, obj]; + const mockedMgetResponse = getMockMgetResponse(registry, [obj1, obj2, obj]); + client.bulk.mockClear(); + client.mget.mockClear(); + client.mget.mockResponseOnce(mockedMgetResponse); + const mockBulkIndexResponse = getMockBulkUpdateResponse(registry, objects); + client.bulk.mockResponseOnce(mockBulkIndexResponse); const result = await repository.bulkUpdate(objects); - expect(client.bulk).toHaveBeenCalledTimes(1); + + expect(client.mget).toHaveBeenCalled(); + expect(client.bulk).toHaveBeenCalled(); + + const expectClientCallObjects = [obj1, obj2]; + expectClientCallArgsAction(expectClientCallObjects, { method: 'index' }); + expect(result).toEqual({ - saved_objects: [expectUpdateResult(obj1), expectError(obj), expectUpdateResult(obj2)], + saved_objects: [expectUpdateResult(obj1), expectUpdateResult(obj2), expectError(obj)], }); }); @@ -515,14 +618,25 @@ describe('SavedObjectsRepository', () => { id: 'three', attributes: {}, }; - const result = await bulkUpdateSuccess( - client, - repository, - registry, - [obj1, obj], - {}, - originId - ); + client.bulk.mockClear(); + client.mget.mockClear(); + const objects = [ + { ...obj1, originId }, + { ...obj, originId }, + ]; + const mockedMgetResponse = getMockMgetResponse(registry, objects); + + client.mget.mockResponseOnce(mockedMgetResponse); + + const mockBulkIndexResponse = getMockBulkUpdateResponse(registry, objects, {}, originId); + client.bulk.mockResponseOnce(mockBulkIndexResponse); + const result = await repository.bulkUpdate(objects); + + expect(client.mget).toHaveBeenCalled(); + expect(client.bulk).toHaveBeenCalled(); + + const expectClientCallObjects = objects; + expectClientCallArgsAction(expectClientCallObjects, { method: 'index' }); expect(result).toEqual({ saved_objects: [ expect.objectContaining({ originId }), diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts index b9c0f10a9021f..9c119ff86e7dd 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.ts @@ -14,6 +14,8 @@ import { DecoratedError, AuthorizeUpdateObject, SavedObjectsRawDoc, + SavedObjectsRawDocSource, + SavedObjectSanitizedDoc, } from '@kbn/core-saved-objects-server'; import { ALL_NAMESPACES_STRING, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import { encodeVersion } from '@kbn/core-saved-objects-base-server-internal'; @@ -35,6 +37,8 @@ import { isLeft, isRight, rawDocExistsInNamespace, + getSavedObjectFromSource, + mergeForUpdate, } from './utils'; import { ApiExecutionContext } from './types'; @@ -43,32 +47,51 @@ export interface PerformUpdateParams { options: SavedObjectsBulkUpdateOptions; } +type DocumentToSave = Record; +type ExpectedBulkGetResult = Either< + { type: string; id: string; error: Payload }, + { + type: string; + id: string; + version?: string; + documentToSave: DocumentToSave; + objectNamespace?: string; + esRequestIndex: number; + migrationVersionCompatibility?: 'raw' | 'compatible'; + } +>; + +type ExpectedBulkUpdateResult = Either< + { type: string; id: string; error: Payload }, + { + type: string; + id: string; + namespaces?: string[]; + documentToSave: DocumentToSave; + esRequestIndex: number; + rawMigratedUpdatedDoc: SavedObjectsRawDoc; + } +>; + export const performBulkUpdate = async ( { objects, options }: PerformUpdateParams, { registry, helpers, allowedTypes, client, serializer, extensions = {} }: ApiExecutionContext ): Promise> => { - const { common: commonHelper, encryption: encryptionHelper } = helpers; + const { + common: commonHelper, + encryption: encryptionHelper, + migration: migrationHelper, + } = helpers; const { securityExtension } = extensions; - + const { migrationVersionCompatibility } = options; const namespace = commonHelper.getCurrentNamespace(options.namespace); const time = getCurrentTime(); let bulkGetRequestIndexCounter = 0; - type DocumentToSave = Record; - type ExpectedBulkGetResult = Either< - { type: string; id: string; error: Payload }, - { - type: string; - id: string; - version?: string; - documentToSave: DocumentToSave; - objectNamespace?: string; - esRequestIndex?: number; - } - >; const expectedBulkGetResults = objects.map((object) => { const { type, id, attributes, references, version, namespace: objectNamespace } = object; let error: DecoratedError | undefined; + if (!allowedTypes.includes(type)) { error = SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } else { @@ -91,21 +114,19 @@ export const performBulkUpdate = async ( ...(Array.isArray(references) && { references }), }; - const requiresNamespacesCheck = registry.isMultiNamespace(object.type); - return right({ type, id, version, documentToSave, objectNamespace, - ...(requiresNamespacesCheck && { esRequestIndex: bulkGetRequestIndexCounter++ }), + esRequestIndex: bulkGetRequestIndexCounter++, + migrationVersionCompatibility, }); }); const validObjects = expectedBulkGetResults.filter(isRight); if (validObjects.length === 0) { - // We only have error results; return early to avoid potentially trying authZ checks for 0 types which would result in an exception. return { // Technically the returned array should only contain SavedObject results, but for errors this is not true (we cast to 'any' below) saved_objects: expectedBulkGetResults.map>( @@ -117,20 +138,25 @@ export const performBulkUpdate = async ( // `objectNamespace` is a namespace string, while `namespace` is a namespace ID. // The object namespace string, if defined, will supersede the operation's namespace ID. const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); + const getNamespaceId = (objectNamespace?: string) => objectNamespace !== undefined ? SavedObjectsUtils.namespaceStringToId(objectNamespace) : namespace; + const getNamespaceString = (objectNamespace?: string) => objectNamespace ?? namespaceString; - const bulkGetDocs = validObjects - .filter(({ value }) => value.esRequestIndex !== undefined) - .map(({ value: { type, id, objectNamespace } }) => ({ - _id: serializer.generateRawId(getNamespaceId(objectNamespace), type, id), - _index: commonHelper.getIndexForType(type), - _source: ['type', 'namespaces'], - })); + + const bulkGetDocs = validObjects.map(({ value: { type, id, objectNamespace } }) => ({ + _id: serializer.generateRawId(getNamespaceId(objectNamespace), type, id), + _index: commonHelper.getIndexForType(type), + _source: true, + })); + const bulkGetResponse = bulkGetDocs.length - ? await client.mget({ body: { docs: bulkGetDocs } }, { ignore: [404], meta: true }) + ? await client.mget( + { body: { docs: bulkGetDocs } }, + { ignore: [404], meta: true } + ) : undefined; // fail fast if we can't verify a 404 response is from Elasticsearch if ( @@ -145,14 +171,24 @@ export const performBulkUpdate = async ( const authObjects: AuthorizeUpdateObject[] = validObjects.map((element) => { const { type, id, objectNamespace, esRequestIndex: index } = element.value; - const preflightResult = index !== undefined ? bulkGetResponse?.body.docs[index] : undefined; - return { - type, - id, - objectNamespace, - // @ts-expect-error MultiGetHit._source is optional - existingNamespaces: preflightResult?._source?.namespaces ?? [], - }; + const preflightResult = bulkGetResponse!.body.docs[index]; + + if (registry.isMultiNamespace(type)) { + return { + type, + id, + objectNamespace, + // @ts-expect-error MultiGetHit._source is optional + existingNamespaces: preflightResult._source?.namespaces ?? [], + }; + } else { + return { + type, + id, + objectNamespace, + existingNamespaces: [], + }; + } }); const authorizationResult = await securityExtension?.authorizeBulkUpdate({ @@ -162,16 +198,7 @@ export const performBulkUpdate = async ( let bulkUpdateRequestIndexCounter = 0; const bulkUpdateParams: object[] = []; - type ExpectedBulkUpdateResult = Either< - { type: string; id: string; error: Payload }, - { - type: string; - id: string; - namespaces: string[]; - documentToSave: DocumentToSave; - esRequestIndex: number; - } - >; + const expectedBulkUpdateResults = await Promise.all( expectedBulkGetResults.map>(async (expectedBulkGetResult) => { if (isLeft(expectedBulkGetResult)) { @@ -181,67 +208,105 @@ export const performBulkUpdate = async ( const { esRequestIndex, id, type, version, documentToSave, objectNamespace } = expectedBulkGetResult.value; - let namespaces; - let versionProperties; - if (esRequestIndex !== undefined) { - const indexFound = bulkGetResponse?.statusCode !== 404; - const actualResult = indexFound ? bulkGetResponse?.body.docs[esRequestIndex] : undefined; - const docFound = indexFound && isMgetDoc(actualResult) && actualResult.found; - if ( - !docFound || + let namespaces: string[] | undefined; + const versionProperties = getExpectedVersionProperties(version); + const indexFound = bulkGetResponse?.statusCode !== 404; + const actualResult = indexFound ? bulkGetResponse?.body.docs[esRequestIndex] : undefined; + const docFound = indexFound && isMgetDoc(actualResult) && actualResult.found; + const isMultiNS = registry.isMultiNamespace(type); + + if ( + !docFound || + (isMultiNS && !rawDocExistsInNamespace( registry, actualResult as SavedObjectsRawDoc, getNamespaceId(objectNamespace) - ) - ) { - return left({ - id, - type, - error: errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id)), - }); - } + )) + ) { + return left({ + id, + type, + error: errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id)), + }); + } + + if (isMultiNS) { // @ts-expect-error MultiGetHit is incorrectly missing _id, _source namespaces = actualResult!._source.namespaces ?? [ // @ts-expect-error MultiGetHit is incorrectly missing _id, _source SavedObjectsUtils.namespaceIdToString(actualResult!._source.namespace), ]; - versionProperties = getExpectedVersionProperties(version); - } else { - if (registry.isSingleNamespace(type)) { - // if `objectNamespace` is undefined, fall back to `options.namespace` - namespaces = [getNamespaceString(objectNamespace)]; - } - versionProperties = getExpectedVersionProperties(version); + } else if (registry.isSingleNamespace(type)) { + // if `objectNamespace` is undefined, fall back to `options.namespace` + namespaces = [getNamespaceString(objectNamespace)]; + } + + const document = getSavedObjectFromSource( + registry, + type, + id, + actualResult as SavedObjectsRawDoc, + { migrationVersionCompatibility } + ); + + let migrated: SavedObject; + try { + migrated = migrationHelper.migrateStorageDocument(document) as SavedObject; + } catch (migrateStorageDocError) { + throw SavedObjectsErrorHelpers.decorateGeneralError( + migrateStorageDocError, + 'Failed to migrate document to the latest version.' + ); } + const typeDefinition = registry.getType(type)!; + const updatedAttributes = mergeForUpdate({ + targetAttributes: { + ...migrated!.attributes, + }, + updatedAttributes: await encryptionHelper.optionallyEncryptAttributes( + type, + id, + objectNamespace || namespace, + documentToSave[type] + ), + typeMappings: typeDefinition.mappings, + }); + + const migratedUpdatedSavedObjectDoc = migrationHelper.migrateInputDocument({ + ...migrated!, + id, + type, + namespace, + namespaces, + attributes: updatedAttributes, + updated_at: time, + ...(Array.isArray(documentToSave.references) && { references: documentToSave.references }), + }); + const updatedMigratedDocumentToSave = serializer.savedObjectToRaw( + migratedUpdatedSavedObjectDoc as SavedObjectSanitizedDoc + ); + const expectedResult = { type, id, namespaces, esRequestIndex: bulkUpdateRequestIndexCounter++, documentToSave: expectedBulkGetResult.value.documentToSave, + rawMigratedUpdatedDoc: updatedMigratedDocumentToSave, + migrationVersionCompatibility, }; bulkUpdateParams.push( { - update: { + index: { _id: serializer.generateRawId(getNamespaceId(objectNamespace), type, id), _index: commonHelper.getIndexForType(type), ...versionProperties, }, }, - { - doc: { - ...documentToSave, - [type]: await encryptionHelper.optionallyEncryptAttributes( - type, - id, - objectNamespace || namespace, - documentToSave[type] - ), - }, - } + updatedMigratedDocumentToSave._source ); return right(expectedResult); @@ -264,7 +329,8 @@ export const performBulkUpdate = async ( return expectedResult.value as any; } - const { type, id, namespaces, documentToSave, esRequestIndex } = expectedResult.value; + const { type, id, namespaces, documentToSave, esRequestIndex, rawMigratedUpdatedDoc } = + expectedResult.value; const response = bulkUpdateResponse?.items[esRequestIndex] ?? {}; const rawResponse = Object.values(response)[0] as any; @@ -273,14 +339,12 @@ export const performBulkUpdate = async ( return { type, id, error }; } - // When a bulk update operation is completed, any fields specified in `_sourceIncludes` will be found in the "get" value of the - // returned object. We need to retrieve the `originId` if it exists so we can return it to the consumer. - const { _seq_no: seqNo, _primary_term: primaryTerm, get } = rawResponse; + const { _seq_no: seqNo, _primary_term: primaryTerm } = rawResponse; // eslint-disable-next-line @typescript-eslint/naming-convention const { [type]: attributes, references, updated_at } = documentToSave; - const { originId } = get._source; + const { originId } = rawMigratedUpdatedDoc._source; return { id, type, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts index fd9c587502d7b..61f9cb4cfdb27 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.ts @@ -106,7 +106,7 @@ export const executeUpdate = async ( preflightDocResult, }); - const existingNamespaces = preflightDocNSResult?.savedObjectNamespaces ?? []; + const existingNamespaces = preflightDocNSResult.savedObjectNamespaces ?? []; const authorizationResult = await securityExtension?.authorizeUpdate({ namespace, object: { type, id, existingNamespaces }, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts index a2b4121cebad0..79e04bae320cd 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.test.ts @@ -235,6 +235,81 @@ describe('createPointInTimeFinder()', () => { ); }); + test('does not yield empty first page', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ + id: 'abc123', + }); + repository.find.mockResolvedValueOnce({ + total: 2, + saved_objects: [], + pit_id: 'abc123', + per_page: 2, + page: 0, + }); + + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { + type: ['visualization'], + search: 'foo*', + }; + + const internalOptions = {}; + const finder = new PointInTimeFinder(findOptions, { + logger, + client: repository, + internalOptions, + }); + + const hits: SavedObjectsFindResult[] = []; + let pageCount = 0; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + pageCount++; + } + + expect(pageCount).toEqual(0); + expect(hits.length).toEqual(0); + }); + + test('yields empty first page if aggregations are used', async () => { + repository.openPointInTimeForType.mockResolvedValueOnce({ + id: 'abc123', + }); + repository.find.mockResolvedValueOnce({ + total: 2, + saved_objects: [], + pit_id: 'abc123', + per_page: 2, + page: 0, + }); + + const findOptions: SavedObjectsCreatePointInTimeFinderOptions = { + type: ['visualization'], + search: 'foo*', + aggs: { + some: { + avg: { field: 'fo' }, + }, + }, + }; + + const internalOptions = {}; + const finder = new PointInTimeFinder(findOptions, { + logger, + client: repository, + internalOptions, + }); + + const hits: SavedObjectsFindResult[] = []; + let pageCount = 0; + for await (const result of finder.find()) { + hits.push(...result.saved_objects); + pageCount++; + } + + expect(pageCount).toEqual(1); + expect(hits.length).toEqual(0); + }); + test('still applies the defaults in the mandatory fields even when `undefined` is explicitly provided', async () => { repository.openPointInTimeForType.mockResolvedValueOnce({ id: 'abc123', diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts index 524b571894007..7a2c124a0e86e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/point_in_time_finder.ts @@ -93,7 +93,12 @@ export class PointInTimeFinder await this.close(); } - yield results; + // do not yield first page if empty, unless there are aggregations + // (in which case we always want to return at least one page) + if (lastResultsCount > 0 || this.#findOptions.aggs) { + yield results; + } + // We've reached the end when there are fewer hits than our perPage size, // or when `close()` has been called. } while (this.#open && lastResultsCount >= this.#findOptions.perPage!); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts index 29983177adc99..82a7d2930f8d5 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts @@ -696,26 +696,23 @@ describe('SavedObjectsRepository Spaces Extension', () => { expect.objectContaining({ body: expect.arrayContaining([ expect.objectContaining({ - update: expect.objectContaining({ + index: expect.objectContaining({ _id: `${ currentSpace.expectedNamespace ? `${currentSpace.expectedNamespace}:` : '' }${obj1.type}:${obj1.id}`, }), }), expect.objectContaining({ - doc: expect.objectContaining({ - config: obj1.attributes, - }), + config: obj1.attributes, }), + expect.objectContaining({ - update: expect.objectContaining({ + index: expect.objectContaining({ _id: `${obj2.type}:${obj2.id}`, }), }), expect.objectContaining({ - doc: expect.objectContaining({ - multiNamespaceType: obj2.attributes, - }), + multiNamespaceType: obj2.attributes, }), ]), }), diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts index 29c00e9d41ac1..d3a31a905de5c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts @@ -469,7 +469,9 @@ export const getMockGetResponse = ( export const getMockMgetResponse = ( registry: SavedObjectTypeRegistry, - objects: Array, + objects: Array< + TypeIdTuple & { found?: boolean; initialNamespaces?: string[]; originId?: string } + >, namespace?: string ) => ({ @@ -649,10 +651,10 @@ export const getMockBulkUpdateResponse = ( objects: TypeIdTuple[], options?: SavedObjectsBulkUpdateOptions, originId?: string -) => - ({ +) => { + return { items: objects.map(({ type, id }) => ({ - update: { + index: { _id: `${ registry.isSingleNamespace(type) && options?.namespace ? `${options?.namespace}:` : '' }${type}:${id}`, @@ -667,7 +669,8 @@ export const getMockBulkUpdateResponse = ( result: 'updated', }, })), - } as estypes.BulkResponse); + } as estypes.BulkResponse; +}; export const bulkUpdateSuccess = async ( client: ElasticsearchClientMock, @@ -678,19 +681,26 @@ export const bulkUpdateSuccess = async ( originId?: string, multiNamespaceSpace?: string // the space for multi namespace objects returned by mock mget (this is only needed for space ext testing) ) => { - const multiNamespaceObjects = objects.filter(({ type }) => registry.isMultiNamespace(type)); - if (multiNamespaceObjects?.length) { - const response = getMockMgetResponse( - registry, - multiNamespaceObjects, - multiNamespaceSpace ?? options?.namespace - ); - client.mget.mockResponseOnce(response); + let mockedMgetResponse; + const validObjects = objects.filter(({ type }) => registry.getType(type) !== undefined); + const multiNamespaceObjects = validObjects.filter(({ type }) => registry.isMultiNamespace(type)); + + if (validObjects?.length) { + if (multiNamespaceObjects.length > 0) { + mockedMgetResponse = getMockMgetResponse( + registry, + validObjects, + multiNamespaceSpace ?? options?.namespace + ); + } else { + mockedMgetResponse = getMockMgetResponse(registry, validObjects); + } + client.mget.mockResponseOnce(mockedMgetResponse); } const response = getMockBulkUpdateResponse(registry, objects, options, originId); client.bulk.mockResponseOnce(response); const result = await repository.bulkUpdate(objects, options); - expect(client.mget).toHaveBeenCalledTimes(multiNamespaceObjects?.length ? 1 : 0); + expect(client.mget).toHaveBeenCalledTimes(validObjects?.length ? 1 : 0); return result; }; diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts index 6d10aee397b2f..49bc8d769a1d6 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_update.ts @@ -39,6 +39,8 @@ export interface SavedObjectsBulkUpdateObject export interface SavedObjectsBulkUpdateOptions extends SavedObjectsBaseOptions { /** The Elasticsearch Refresh setting for this operation */ refresh?: MutatingOperationRefreshSetting; + /** {@link SavedObjectsRawDocParseOptions.migrationVersionCompatibility} */ + migrationVersionCompatibility?: 'compatible' | 'raw'; } /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts index 532c2dc58d992..1d2d7225f5d84 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts @@ -276,6 +276,13 @@ export interface SavedObjectsClientContract { /** * Bulk Updates multiple SavedObject at once * + * The savedObjects `bulkUpdate` API will update documents client-side and then reindex the updated documents. + * These update operations are done in-memory, and cause memory constraint issues when + * updating many objects with large `json` blobs stored in some fields. As such, we recommend against using + * `bulkUpdate` for savedObjects that: + * - use arrays (as these tend to be large objects) + * - store large `json` blobs in some fields + * * @param objects - array of objects to update (contains ID, type, attributes, and optional namespace) * @param options {@link SavedObjectsBulkUpdateOptions} - options for the bulkUpdate operation * @returns the {@link SavedObjectsBulkUpdateResponse} diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts index eea0966433f02..2a3535f19207c 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts @@ -42,7 +42,14 @@ export type { MigrationStatus, MigrateDocumentOptions, } from './src/migration'; -export { parseObjectKey, getObjectKey, getIndexForType } from './src/utils'; +export { + parseObjectKey, + getObjectKey, + getIndexForType, + getFieldListFromTypeMapping, + getFieldListMapFromMappingDefinitions, + type FieldListMap, +} from './src/utils'; export { modelVersionVirtualMajor, globalSwitchToModelVersionAt, @@ -68,4 +75,6 @@ export { buildModelVersionTransformFn, aggregateMappingAdditions, convertModelVersionBackwardConversionSchema, + getVersionAddedMappings, + getVersionAddedFields, } from './src/model_version'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts index 350ab86ca2980..3c189977731e3 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts @@ -37,3 +37,4 @@ export { getModelVersionDelta } from './get_version_delta'; export { buildModelVersionTransformFn } from './build_transform_fn'; export { aggregateMappingAdditions } from './aggregate_model_changes'; export { convertModelVersionBackwardConversionSchema } from './backward_conversion_schema'; +export { getVersionAddedFields, getVersionAddedMappings } from './version_mapping_changes'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.test.ts new file mode 100644 index 0000000000000..00946f4dc1eb0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.test.ts @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + SavedObjectsModelVersion, + SavedObjectsModelChange, +} from '@kbn/core-saved-objects-server'; +import { getVersionAddedMappings, getVersionAddedFields } from './version_mapping_changes'; + +const createVersion = (changes: SavedObjectsModelChange[]): SavedObjectsModelVersion => { + return { + changes, + }; +}; + +describe('getVersionAddedMappings', () => { + it('returns empty mappings when the version has no changes', () => { + const version = createVersion([]); + expect(getVersionAddedMappings(version)).toEqual({}); + }); + + it('returns empty mappings when the version has no `mappings_addition` changes', () => { + const version = createVersion([ + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + ]); + expect(getVersionAddedMappings(version)).toEqual({}); + }); + + it(`returns the change's mappings when the version has a single 'mappings_addition' changes`, () => { + const version = createVersion([ + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + { + type: 'mappings_addition', + addedMappings: { + nested: { + properties: { + foo: { type: 'text' }, + }, + }, + }, + }, + ]); + expect(getVersionAddedMappings(version)).toEqual({ + nested: { + properties: { + foo: { type: 'text' }, + }, + }, + }); + }); + + it(`merges the mappings when the version has multiple 'mappings_addition' changes`, () => { + const version = createVersion([ + { + type: 'mappings_addition', + addedMappings: { + top: { type: 'text' }, + nested: { + properties: { + bar: { type: 'text' }, + }, + }, + }, + }, + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + { + type: 'mappings_addition', + addedMappings: { + nested: { + properties: { + foo: { type: 'text' }, + }, + }, + }, + }, + ]); + expect(getVersionAddedMappings(version)).toEqual({ + top: { type: 'text' }, + nested: { + properties: { + foo: { type: 'text' }, + bar: { type: 'text' }, + }, + }, + }); + }); +}); + +describe('getVersionAddedFields', () => { + it('returns empty mappings when the version has no changes', () => { + const version = createVersion([]); + expect(getVersionAddedFields(version)).toEqual([]); + }); + + it('returns empty mappings when the version has no `mappings_addition` changes', () => { + const version = createVersion([ + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + ]); + expect(getVersionAddedFields(version)).toEqual([]); + }); + + it(`returns the change's mappings when the version has a single 'mappings_addition' changes`, () => { + const version = createVersion([ + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + { + type: 'mappings_addition', + addedMappings: { + nested: { + properties: { + foo: { type: 'text' }, + }, + }, + }, + }, + ]); + expect(getVersionAddedFields(version)).toEqual(['nested', 'nested.foo']); + }); + + it(`merges the mappings when the version has multiple 'mappings_addition' changes`, () => { + const version = createVersion([ + { + type: 'mappings_addition', + addedMappings: { + top: { type: 'text' }, + nested: { + properties: { + bar: { type: 'text' }, + }, + }, + }, + }, + { + type: 'data_backfill', + backfillFn: jest.fn(), + }, + { + type: 'mappings_addition', + addedMappings: { + nested: { + properties: { + foo: { type: 'text' }, + }, + }, + }, + }, + ]); + expect(getVersionAddedFields(version)).toEqual(['nested', 'nested.bar', 'nested.foo', 'top']); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.ts new file mode 100644 index 0000000000000..adbbf74ae79cf --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_mapping_changes.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 { merge } from 'lodash'; +import type { + SavedObjectsMappingProperties, + SavedObjectsModelVersion, + SavedObjectsModelMappingsAdditionChange, +} from '@kbn/core-saved-objects-server'; +import { getFieldListFromTypeMapping } from '../utils/get_field_list'; + +/** + * Return the mappings that were introduced in the given version. + * If multiple 'mappings_addition' changes are present for the version, + * they will be deep-merged. + */ +export const getVersionAddedMappings = ( + version: SavedObjectsModelVersion +): SavedObjectsMappingProperties => { + const mappingChanges = version.changes.filter( + (change) => change.type === 'mappings_addition' + ) as SavedObjectsModelMappingsAdditionChange[]; + return merge({}, ...mappingChanges.map((change) => change.addedMappings)); +}; + +/** + * Return the list of fields, sorted, that were introduced in the given version. + */ +export const getVersionAddedFields = (version: SavedObjectsModelVersion): string[] => { + const addedMappings = getVersionAddedMappings(version); + return getFieldListFromTypeMapping({ properties: addedMappings }); +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.test.ts new file mode 100644 index 0000000000000..ef1a8e1e0164f --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { SavedObjectsTypeMappingDefinition } from '@kbn/core-saved-objects-server'; +import { getFieldListFromTypeMapping } from './get_field_list'; + +describe('getFieldListFromTypeMapping', () => { + it('returns an empty list for empty mappings', () => { + const mappings: SavedObjectsTypeMappingDefinition = { + properties: {}, + }; + expect(getFieldListFromTypeMapping(mappings)).toEqual([]); + }); + + it('returns the correct list for top level fields', () => { + const mappings: SavedObjectsTypeMappingDefinition = { + properties: { + foo: { type: 'text' }, + bar: { type: 'text' }, + }, + }; + expect(getFieldListFromTypeMapping(mappings)).toEqual(['bar', 'foo']); + }); + + it('returns the correct list for deep fields', () => { + const mappings: SavedObjectsTypeMappingDefinition = { + properties: { + foo: { + properties: { + hello: { type: 'text' }, + dolly: { type: 'text' }, + }, + }, + bar: { type: 'text' }, + }, + }; + expect(getFieldListFromTypeMapping(mappings)).toEqual(['bar', 'foo', 'foo.dolly', 'foo.hello']); + }); + + it('returns the correct list for any depth', () => { + const mappings: SavedObjectsTypeMappingDefinition = { + properties: { + foo: { + properties: { + hello: { type: 'text' }, + dolly: { + properties: { + far: { type: 'text' }, + }, + }, + }, + }, + bar: { type: 'text' }, + }, + }; + expect(getFieldListFromTypeMapping(mappings)).toEqual([ + 'bar', + 'foo', + 'foo.dolly', + 'foo.dolly.far', + 'foo.hello', + ]); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.ts new file mode 100644 index 0000000000000..8a33d42d7cb01 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/get_field_list.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { MappingProperty as EsMappingProperty } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { + SavedObjectsTypeMappingDefinition, + SavedObjectsFieldMapping, +} from '@kbn/core-saved-objects-server'; +import type { SavedObjectsTypeMappingDefinitions } from '../mappings'; + +export type FieldListMap = Record; + +/** + * Return the list of fields present in each individual type mappings present in the definition. + */ +export const getFieldListMapFromMappingDefinitions = ( + mappings: SavedObjectsTypeMappingDefinitions +): FieldListMap => { + return Object.entries(mappings).reduce((memo, [typeName, typeMappings]) => { + memo[typeName] = getFieldListFromTypeMapping(typeMappings); + return memo; + }, {}); +}; + +type AnyFieldMapping = SavedObjectsFieldMapping | EsMappingProperty; + +interface QueueItem { + fieldPath: string[]; + fieldDef: AnyFieldMapping; +} + +/** + * Return the list of fields present in the provided mappings. + * Note that fields only containing properties are still considered fields by this function. + * + * @example + * ``` + * getFieldListFromTypeMapping({ + * properties: { + * foo: { + * properties: { + * hello: { type: 'text' }, + * dolly: { type: 'text' }, + * }, + * }, + * }, + * }); + * // ['foo', 'foo.dolly', 'foo.hello'] + * ``` + */ +export const getFieldListFromTypeMapping = ( + typeMappings: SavedObjectsTypeMappingDefinition +): string[] => { + const fieldList: string[] = []; + const queue: QueueItem[] = []; + + Object.entries(typeMappings.properties).forEach(([fieldName, fieldDef]) => { + queue.push({ + fieldPath: [fieldName], + fieldDef, + }); + }); + + while (queue.length > 0) { + const item = queue.pop()!; + fieldList.push(item.fieldPath.join('.')); + if ('properties' in item.fieldDef) { + Object.entries(item.fieldDef.properties ?? {}).forEach(([fieldName, fieldDef]) => { + queue.push({ + fieldPath: [...item.fieldPath, fieldName], + fieldDef, + }); + }); + } + } + + return fieldList.sort(); +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/index.ts index 8db62a4c52280..374b39abe9edb 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/utils/index.ts @@ -8,3 +8,8 @@ export { getObjectKey, parseObjectKey } from './object_key'; export { getIndexForType } from './get_index_for_type'; +export { + getFieldListFromTypeMapping, + getFieldListMapFromMappingDefinitions, + type FieldListMap, +} from './get_field_list'; diff --git a/packages/core/saved-objects/core-saved-objects-server/docs/model_versions.md b/packages/core/saved-objects/core-saved-objects-server/docs/model_versions.md index 0177c85f484ca..31229e5eebc9b 100644 --- a/packages/core/saved-objects/core-saved-objects-server/docs/model_versions.md +++ b/packages/core/saved-objects/core-saved-objects-server/docs/model_versions.md @@ -1040,76 +1040,13 @@ to the `fields` option **were already present in the prior model version**. Othe during upgrades, where newly introduced or backfilled fields may not necessarily appear in the documents returned from the `search` API when the option is used. -### Using `bulkUpdate` with dynamically backfilled fields + (*note*: both the previous and next version of Kibana must follow this rule then) -(Note: this same limitation used to exist for the `update` method but has been [fixed](https://github.com/elastic/kibana/issues/165434). So while they're similar this limitation is only relevant for the `bulkUpdate` method) +### Using `bulkUpdate` for fields with large `json` blobs -The savedObjects `bulkUpdate` API is effectively a partial update (using Elasticsearch's `_update` under the hood), -allowing API consumers to only specify the subset of fields they want to update to new values, without having to -provide the full list of attributes (the unchanged ones). We're also not changing the `version` of the document -during updates, even when the instance performing the operation doesn't know about the current model version -of the document (e.g an old node during an upgrade). - -If this was fine before zero downtime upgrades, there is an edge case in serverless when this API is used -to update fields that are the "source" of another field's backfill that can potentially lead to data becoming inconsistent. - -For example, imagine that: - -1. In model version 1, we have some `index (number)` field. - -2. In model version 2, we introduce a `odd (boolean)` field that is backfilled with the following function: - -```ts -let change: SavedObjectsModelDataBackfillChange = { - type: 'data_backfill', - backfillFn: (doc, ctx) => { - return { attributes: { odd: doc.attributes.index % 2 === 1 } }; - }, -}; -``` - -3. During the cohabitation period (upgrade), an instance of the new version of Kibana creates a document - -E.g with the following attributes: - -```ts -const newDocAttributes = { - index: 12, - odd: false, -} -``` - -4. Then an instance of the old version of Kibana updates the `index` field of this document - -Which could occur either while being still in the cohabitation period, or in case of rollback: - -```ts -savedObjectClient.bulkUpdate({ - objects: [{ - type: 'type', - id: 'id', - attributes: { - index: 11 - } - }] -}); -``` - -We will then be in a situation where our data is **inconsistent**, as the value of the `odd` field wasn't recomputed: - -```json -{ - index: 11, - odd: false, -} -``` - -The long term solution for that is implementing [backward-compatible updates](https://github.com/elastic/kibana/issues/165434), however -this won't be done for the MVP, so the workaround for now is to avoid situations where this edge case can occur. - -It can be avoided by either: - -1. Not having backfill functions depending on the value of the existing fields (*recommended*) - -2. Not performing update operations impacting fields that are used as "source" for backfill functions - (*note*: both the previous and next version of Kibana must follow this rule then) \ No newline at end of file +The savedObjects `bulkUpdate` API will update documents client-side and then reindex the updated documents. +These update operations are done in-memory, and cause memory constraint issues when +updating many objects with large `json` blobs stored in some fields. As such, we recommend against using +`bulkUpdate` for savedObjects that: +- use arrays (as these tend to be large objects) +- store large `json` blobs in some fields diff --git a/packages/core/saved-objects/docs/openapi/bundled.json b/packages/core/saved-objects/docs/openapi/bundled.json index dd6b95fa959b2..4382ee9b62d98 100644 --- a/packages/core/saved-objects/docs/openapi/bundled.json +++ b/packages/core/saved-objects/docs/openapi/bundled.json @@ -757,7 +757,7 @@ "post": { "summary": "Resolve errors from the Import objects API.", "operationId": "resolveImportErrors", - "description": "To resolve errors, you can: \n\n* Retry certain saved objects\n* Overwrite specific saved objects\n* Change references to different saved objects\n\nThis functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", + "description": "To resolve errors, you can:\n\n* Retry certain saved objects\n* Overwrite specific saved objects\n* Change references to different saved objects\n\nThis functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.\n", "tags": [ "saved objects" ], @@ -1210,7 +1210,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "examples": { diff --git a/packages/core/saved-objects/docs/openapi/bundled.yaml b/packages/core/saved-objects/docs/openapi/bundled.yaml index 3e07633a5107b..a12e326ccdbe4 100644 --- a/packages/core/saved-objects/docs/openapi/bundled.yaml +++ b/packages/core/saved-objects/docs/openapi/bundled.yaml @@ -208,7 +208,7 @@ paths: responses: '200': description: | - Indicates a successful call. NOTE: This HTTP response code indicates that the bulk operation succeeded. Errors pertaining to individual objects will be returned in the response body. + Indicates a successful call. NOTE: This HTTP response code indicates that the bulk operation succeeded. Errors pertaining to individual objects will be returned in the response body. content: application/json: schema: @@ -239,7 +239,7 @@ paths: responses: '200': description: | - Indicates a successful call. NOTE: This HTTP response code indicates that the bulk operation succeeded. Errors pertaining to individual objects will be returned in the response body. + Indicates a successful call. NOTE: This HTTP response code indicates that the bulk operation succeeded. Errors pertaining to individual objects will be returned in the response body. content: application/json: schema: @@ -798,7 +798,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' examples: key_rotation_response: summary: Encryption key rotation using default parameters. diff --git a/packages/core/saved-objects/docs/openapi/entrypoint.yaml b/packages/core/saved-objects/docs/openapi/entrypoint.yaml index cfb8a93210a50..b4e58a6ef2171 100644 --- a/packages/core/saved-objects/docs/openapi/entrypoint.yaml +++ b/packages/core/saved-objects/docs/openapi/entrypoint.yaml @@ -49,7 +49,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - - apiKeyAuth: [] \ No newline at end of file + - apiKeyAuth: [] diff --git a/packages/core/status/core-status-server-internal/src/plugins_status.test.ts b/packages/core/status/core-status-server-internal/src/plugins_status.test.ts index d1d35ade56159..ed14eee45229e 100644 --- a/packages/core/status/core-status-server-internal/src/plugins_status.test.ts +++ b/packages/core/status/core-status-server-internal/src/plugins_status.test.ts @@ -41,7 +41,7 @@ describe('PluginStatusService', () => { pluginDependencies, }); - service.blockNewRegistrations(); + service.start(); expect(() => { service.set( 'a', @@ -365,6 +365,8 @@ describe('PluginStatusService', () => { const pluginA$ = new ReplaySubject(1); service.set('a', pluginA$); + service.start(); // the plugin emission timeout starts counting when we call pluginsStatus.start() + // the first emission happens right after core$ services emit const firstEmission = firstValueFrom(service.getAll$().pipe(skip(1))); diff --git a/packages/core/status/core-status-server-internal/src/plugins_status.ts b/packages/core/status/core-status-server-internal/src/plugins_status.ts index 8d20eff18927c..7de22e23cc97e 100644 --- a/packages/core/status/core-status-server-internal/src/plugins_status.ts +++ b/packages/core/status/core-status-server-internal/src/plugins_status.ts @@ -6,15 +6,22 @@ * Side Public License, v 1. */ -import { BehaviorSubject, type Observable, ReplaySubject, type Subscription } from 'rxjs'; +import { + BehaviorSubject, + merge, + Observable, + ReplaySubject, + Subject, + type Subscription, +} from 'rxjs'; import { map, distinctUntilChanged, filter, - timeout, - startWith, tap, debounceTime, + takeUntil, + delay, } from 'rxjs/operators'; import { sortBy } from 'lodash'; import { isDeepStrictEqual } from 'util'; @@ -62,6 +69,7 @@ export class PluginsStatusService { private pluginData: PluginData; private rootPlugins: PluginName[]; // root plugins are those that do not have any dependencies private orderedPluginNames: PluginName[]; + private start$ = new Subject(); private pluginData$ = new ReplaySubject(1); private pluginStatus: PluginsStatus = {}; private pluginStatus$ = new BehaviorSubject(this.pluginStatus); @@ -110,26 +118,21 @@ export class PluginsStatusService { // delete any derived statuses calculated before the custom status Observable was registered delete this.pluginStatus[plugin]; - const statusChanged$ = status$.pipe(distinctUntilChanged()); + const firstEmissionTimeout$ = this.start$.pipe( + delay(this.statusTimeoutMs), + map(() => ({ + level: ServiceStatusLevels.unavailable, + summary: `Status check timed out after ${ + this.statusTimeoutMs < 1000 + ? `${this.statusTimeoutMs}ms` + : `${this.statusTimeoutMs / 1000}s` + }`, + })), + takeUntil(status$) + ); - this.reportedStatusSubscriptions[plugin] = statusChanged$ - .pipe( - // Set a timeout for externally-defined status Observables - timeout({ - first: this.statusTimeoutMs, - with: () => - statusChanged$.pipe( - startWith({ - level: ServiceStatusLevels.unavailable, - summary: `Status check timed out after ${ - this.statusTimeoutMs < 1000 - ? `${this.statusTimeoutMs}ms` - : `${this.statusTimeoutMs / 1000}s` - }`, - }) - ), - }) - ) + this.reportedStatusSubscriptions[plugin] = merge(firstEmissionTimeout$, status$) + .pipe(distinctUntilChanged()) .subscribe((status) => { const { levelChanged, summaryChanged } = this.updatePluginReportedStatus(plugin, status); @@ -143,11 +146,11 @@ export class PluginsStatusService { }); } - /** - * Prevent plugins from registering status Observables - */ - public blockNewRegistrations() { + public start() { + // Prevent plugins from registering status Observables this.newRegistrationsAllowed = false; + this.start$.next(); + this.start$.complete(); } /** diff --git a/packages/core/status/core-status-server-internal/src/status_service.ts b/packages/core/status/core-status-server-internal/src/status_service.ts index 0d1a53615da5f..f33621e101a7d 100644 --- a/packages/core/status/core-status-server-internal/src/status_service.ts +++ b/packages/core/status/core-status-server-internal/src/status_service.ts @@ -179,7 +179,7 @@ export class StatusService implements CoreService { if (!this.pluginsStatus || !this.overall$) { throw new Error(`StatusService#setup must be called before #start`); } - this.pluginsStatus.blockNewRegistrations(); + this.pluginsStatus.start(); this.logStatusChanges(); } diff --git a/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts index f22e902bbbeaa..8e08439e7450a 100644 --- a/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/field_maps/alert_field_map.ts @@ -41,6 +41,7 @@ import { EVENT_KIND, TAGS, } from '@kbn/rule-data-utils'; +import { MultiField } from './types'; export const alertFieldMap = { [ALERT_ACTION_GROUP]: { @@ -92,6 +93,13 @@ export const alertFieldMap = { type: 'keyword', array: false, required: false, + multi_fields: [ + { + flat_name: `${ALERT_REASON}.text`, + name: 'text', + type: 'match_only_text', + }, + ] as MultiField[], }, [ALERT_RULE_CATEGORY]: { type: 'keyword', 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 index 3704edee72701..8aea9ca56e029 100644 --- 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 @@ -11,9 +11,64 @@ import { EcsMetadata, FieldMap } from './types'; const EXCLUDED_TYPES = ['constant_keyword']; +// ECS fields that have reached Stage 2 in the RFC process +// are included in the generated Yaml but are still considered +// experimental. Some are correctly marked as beta but most are +// not. + +// More about the RFC stages here: https://elastic.github.io/ecs/stages.html + +// The following RFCS are currently in stage 2: +// https://github.com/elastic/ecs/blob/main/rfcs/text/0027-faas-fields.md +// https://github.com/elastic/ecs/blob/main/rfcs/text/0035-tty-output.md +// https://github.com/elastic/ecs/blob/main/rfcs/text/0037-host-metrics.md +// https://github.com/elastic/ecs/blob/main/rfcs/text/0040-volume-device.md + +// Fields from these RFCs that are not already in the ECS component template +// as of 8.11 are manually identified as experimental below. +// The next time this list is updated, we should check the above list of RFCs to +// see if any have moved to Stage 3 and remove them from the list and check if +// there are any new stage 2 RFCs with fields we should exclude as experimental. + +const EXPERIMENTAL_FIELDS = [ + 'faas.trigger', // this was previously mapped as nested but changed to object + 'faas.trigger.request_id', + 'faas.trigger.type', + 'host.cpu.system.norm.pct', + 'host.cpu.user.norm.pct', + 'host.fsstats.total_size.total', + 'host.fsstats.total_size.used', + 'host.fsstats.total_size.used.pct', + 'host.load.norm.1', + 'host.load.norm.5', + 'host.load.norm.15', + 'host.memory.actual.used.bytes', + 'host.memory.actual.used.pct', + 'host.memory.total', + 'process.io.bytes', + 'volume.bus_type', + 'volume.default_access', + 'volume.device_name', + 'volume.device_type', + 'volume.dos_name', + 'volume.file_system_type', + 'volume.mount_name', + 'volume.nt_name', + 'volume.product_id', + 'volume.product_name', + 'volume.removable', + 'volume.serial_number', + 'volume.size', + 'volume.vendor_id', + 'volume.vendor_name', + 'volume.writable', +]; + export const ecsFieldMap: FieldMap = Object.fromEntries( Object.entries(EcsFlat) - .filter(([_, value]) => !EXCLUDED_TYPES.includes(value.type)) + .filter( + ([key, value]) => !EXCLUDED_TYPES.includes(value.type) && !EXPERIMENTAL_FIELDS.includes(key) + ) .map(([key, _]) => { const value: EcsMetadata = EcsFlat[key as keyof typeof EcsFlat]; return [ diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts index a3d3ef6f0a8a7..b3bc0eb161720 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts @@ -308,7 +308,6 @@ const EcsOptional = rt.partial({ 'faas.execution': schemaString, 'faas.id': schemaString, 'faas.name': schemaString, - 'faas.trigger': schemaUnknown, 'faas.version': schemaString, 'file.accessed': schemaDate, 'file.attributes': schemaStringArray, diff --git a/packages/kbn-axe-config/index.ts b/packages/kbn-axe-config/index.ts index 7f73408733720..74cfa52939d88 100644 --- a/packages/kbn-axe-config/index.ts +++ b/packages/kbn-axe-config/index.ts @@ -26,21 +26,11 @@ export const AXE_CONFIG = { id: 'aria-roles', selector: '[data-test-subj="comboBoxSearchInput"] *', }, - { - // EUI bug: https://github.com/elastic/eui/issues/4474 - id: 'aria-required-parent', - selector: '[class=*"euiDataGridRowCell"][role="gridcell"]', - }, { // 3rd-party library; button has aria-describedby id: 'button-name', selector: '[data-rbd-drag-handle-draggable-id]', }, - { - // EUI bug: https://github.com/elastic/eui/issues/4536 - id: 'duplicate-id', - selector: '.euiSuperDatePicker *', - }, ], }; diff --git a/packages/kbn-calculate-width-from-char-count/.storybook/main.js b/packages/kbn-calculate-width-from-char-count/.storybook/main.js new file mode 100644 index 0000000000000..8dc3c5d1518f4 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/packages/kbn-calculate-width-from-char-count/README.md b/packages/kbn-calculate-width-from-char-count/README.md new file mode 100644 index 0000000000000..13581e81bd9e6 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/README.md @@ -0,0 +1,3 @@ +# @kbn/calculate-width-from-char-count + +This package contains a function that calculates the approximate width of the component from a text length. diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/index.ts b/packages/kbn-calculate-width-from-char-count/index.ts similarity index 87% rename from packages/shared-ux/chrome/navigation/src/ui/hooks/index.ts rename to packages/kbn-calculate-width-from-char-count/index.ts index 631ad5f590ce4..de0577ee3ed83 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/index.ts +++ b/packages/kbn-calculate-width-from-char-count/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { useInitNavNode } from './use_init_navnode'; +export * from './src'; diff --git a/packages/kbn-calculate-width-from-char-count/jest.config.js b/packages/kbn-calculate-width-from-char-count/jest.config.js new file mode 100644 index 0000000000000..0538847bfc820 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-calculate-width-from-char-count'], +}; diff --git a/packages/kbn-calculate-width-from-char-count/kibana.jsonc b/packages/kbn-calculate-width-from-char-count/kibana.jsonc new file mode 100644 index 0000000000000..216b12ddeac89 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/calculate-width-from-char-count", + "owner": "@elastic/kibana-visualizations" +} diff --git a/packages/kbn-calculate-width-from-char-count/package.json b/packages/kbn-calculate-width-from-char-count/package.json new file mode 100644 index 0000000000000..dd8182452f0ee --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/calculate-width-from-char-count", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false +} \ No newline at end of file diff --git a/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.test.ts b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.test.ts new file mode 100644 index 0000000000000..1dbe25306b639 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.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 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 { calculateWidthFromCharCount, MAX_WIDTH } from './calculate_width_from_char_count'; + +describe('calculateWidthFromCharCount', () => { + it('should return minimum width if char count is smaller than minWidth', () => { + expect(calculateWidthFromCharCount(10, { minWidth: 300 })).toBe(300); + }); + it('should return calculated width', () => { + expect(calculateWidthFromCharCount(30)).toBe(30 * 7 + 116); + }); + it('should return maximum width if char count is bigger than maxWidth', () => { + expect(calculateWidthFromCharCount(1000)).toBe(MAX_WIDTH); + }); +}); diff --git a/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.ts b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.ts new file mode 100644 index 0000000000000..c79307473c7e8 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_char_count.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. + */ + +export interface LIMITS { + paddingsWidth: number; + minWidth?: number; + avCharWidth: number; + maxWidth: number; +} + +export const MAX_WIDTH = 550; +const PADDINGS_WIDTH = 116; +const AVERAGE_CHAR_WIDTH = 7; + +const defaultPanelWidths: LIMITS = { + maxWidth: MAX_WIDTH, + avCharWidth: AVERAGE_CHAR_WIDTH, + paddingsWidth: PADDINGS_WIDTH, +}; + +export function calculateWidthFromCharCount( + labelLength: number, + overridesPanelWidths?: Partial +) { + const { maxWidth, avCharWidth, paddingsWidth, minWidth } = { + ...defaultPanelWidths, + ...overridesPanelWidths, + }; + const widthForCharCount = paddingsWidth + labelLength * avCharWidth; + + if (minWidth && widthForCharCount < minWidth) { + return minWidth; + } + + return Math.min(widthForCharCount, maxWidth); +} diff --git a/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.test.ts b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.test.ts new file mode 100644 index 0000000000000..6e740defdce92 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { calculateWidthFromEntries } from './calculate_width_from_entries'; +import { MAX_WIDTH } from './calculate_width_from_char_count'; +import faker from 'faker'; + +const generateLabel = (length: number) => faker.random.alpha({ count: length }); + +const generateObjectWithLabelOfLength = (length: number, propOverrides?: Record) => ({ + label: generateLabel(length), + ...propOverrides, +}); + +describe('calculateWidthFromEntries', () => { + it('calculates width for array of strings', () => { + const shortLabels = [10, 20].map(generateLabel); + expect(calculateWidthFromEntries(shortLabels)).toBe(256); + + const mediumLabels = [50, 55, 10, 20].map(generateLabel); + expect(calculateWidthFromEntries(mediumLabels)).toBe(501); + + const longLabels = [80, 90, 10].map(generateLabel); + expect(calculateWidthFromEntries(longLabels)).toBe(MAX_WIDTH); + }); + + it('calculates width for array of objects with keys', () => { + const shortLabels = [10, 20].map((v) => generateObjectWithLabelOfLength(v)); + expect(calculateWidthFromEntries(shortLabels, ['label'])).toBe(256); + + const mediumLabels = [50, 55, 10, 20].map((v) => generateObjectWithLabelOfLength(v)); + expect(calculateWidthFromEntries(mediumLabels, ['label'])).toBe(501); + + const longLabels = [80, 90, 10].map((v) => generateObjectWithLabelOfLength(v)); + expect(calculateWidthFromEntries(longLabels, ['label'])).toBe(MAX_WIDTH); + }); + it('calculates width for array of objects for fallback keys', () => { + const shortLabels = [10, 20].map((v) => + generateObjectWithLabelOfLength(v, { label: undefined, name: generateLabel(v) }) + ); + expect(calculateWidthFromEntries(shortLabels, ['id', 'label', 'name'])).toBe(256); + + const mediumLabels = [50, 55, 10, 20].map((v) => + generateObjectWithLabelOfLength(v, { label: undefined, name: generateLabel(v) }) + ); + expect(calculateWidthFromEntries(mediumLabels, ['id', 'label', 'name'])).toBe(501); + }); +}); diff --git a/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts new file mode 100644 index 0000000000000..4a6795c8ea077 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/src/calculate_width_from_entries.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LIMITS, calculateWidthFromCharCount } from './calculate_width_from_char_count'; + +type GenericObject> = T; + +const getMaxLabelLengthForObjects = ( + entries: GenericObject[], + labelKeys: Array +) => + entries.reduce((acc, curr) => { + const labelKey = labelKeys.find((key) => curr[key]); + if (!labelKey) { + return acc; + } + const labelLength = curr[labelKey].length; + return acc > labelLength ? acc : labelLength; + }, 0); + +const getMaxLabelLengthForStrings = (arr: string[]) => + arr.reduce((acc, curr) => (acc > curr.length ? acc : curr.length), 0); + +export function calculateWidthFromEntries( + entries: GenericObject[] | string[], + labelKeys?: Array, + overridesPanelWidths?: Partial +) { + const maxLabelLength = labelKeys + ? getMaxLabelLengthForObjects(entries as GenericObject[], labelKeys) + : getMaxLabelLengthForStrings(entries as string[]); + + return calculateWidthFromCharCount(maxLabelLength, overridesPanelWidths); +} diff --git a/packages/kbn-subscription-tracking/index.ts b/packages/kbn-calculate-width-from-char-count/src/index.ts similarity index 65% rename from packages/kbn-subscription-tracking/index.ts rename to packages/kbn-calculate-width-from-char-count/src/index.ts index de17c595918d5..33fcddecf7403 100644 --- a/packages/kbn-subscription-tracking/index.ts +++ b/packages/kbn-calculate-width-from-char-count/src/index.ts @@ -6,12 +6,6 @@ * Side Public License, v 1. */ -export * from './src/subscription_elements'; +export { calculateWidthFromCharCount } from './calculate_width_from_char_count'; -export { - SubscriptionTrackingContext, - SubscriptionTrackingProvider, - registerEvents, -} from './src/services'; - -export * from './types'; +export { calculateWidthFromEntries } from './calculate_width_from_entries'; diff --git a/packages/kbn-calculate-width-from-char-count/tsconfig.json b/packages/kbn-calculate-width-from-char-count/tsconfig.json new file mode 100644 index 0000000000000..ea0a30fa75171 --- /dev/null +++ b/packages/kbn-calculate-width-from-char-count/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + ], + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [], + "exclude": [ + "target/**/*", + ] +} diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json new file mode 100644 index 0000000000000..ad25525f19303 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -0,0 +1,970 @@ +{ + "core-usage-stats": [], + "legacy-url-alias": [ + "disabled", + "resolveCounter", + "sourceId", + "targetId", + "targetNamespace", + "targetType" + ], + "config": [ + "buildNum" + ], + "config-global": [ + "buildNum" + ], + "url": [ + "accessDate", + "createDate", + "slug" + ], + "usage-counters": [ + "domainId" + ], + "task": [ + "attempts", + "enabled", + "ownerId", + "retryAt", + "runAt", + "schedule", + "schedule.interval", + "scheduledAt", + "scope", + "status", + "taskType" + ], + "guided-onboarding-guide-state": [ + "guideId", + "isActive" + ], + "guided-onboarding-plugin-state": [], + "ui-metric": [ + "count" + ], + "application_usage_totals": [], + "application_usage_daily": [ + "timestamp" + ], + "event_loop_delays_daily": [ + "lastUpdatedAt" + ], + "index-pattern": [ + "name", + "title", + "type" + ], + "sample-data-telemetry": [ + "installCount", + "unInstallCount" + ], + "space": [ + "name" + ], + "spaces-usage-stats": [], + "exception-list-agnostic": [ + "_tags", + "comments", + "comments.comment", + "comments.created_at", + "comments.created_by", + "comments.id", + "comments.updated_at", + "comments.updated_by", + "created_at", + "created_by", + "description", + "entries", + "entries.entries", + "entries.entries.field", + "entries.entries.operator", + "entries.entries.type", + "entries.entries.value", + "entries.field", + "entries.list", + "entries.list.id", + "entries.list.type", + "entries.operator", + "entries.type", + "entries.value", + "expire_time", + "immutable", + "item_id", + "list_id", + "list_type", + "meta", + "name", + "os_types", + "tags", + "tie_breaker_id", + "type", + "updated_by", + "version" + ], + "exception-list": [ + "_tags", + "comments", + "comments.comment", + "comments.created_at", + "comments.created_by", + "comments.id", + "comments.updated_at", + "comments.updated_by", + "created_at", + "created_by", + "description", + "entries", + "entries.entries", + "entries.entries.field", + "entries.entries.operator", + "entries.entries.type", + "entries.entries.value", + "entries.field", + "entries.list", + "entries.list.id", + "entries.list.type", + "entries.operator", + "entries.type", + "entries.value", + "expire_time", + "immutable", + "item_id", + "list_id", + "list_type", + "meta", + "name", + "os_types", + "tags", + "tie_breaker_id", + "type", + "updated_by", + "version" + ], + "telemetry": [], + "file": [ + "FileKind", + "Meta", + "Status", + "Updated", + "created", + "extension", + "hash", + "mime_type", + "name", + "size", + "user" + ], + "fileShare": [ + "created", + "name", + "token", + "valid_until" + ], + "action": [ + "actionTypeId", + "name" + ], + "action_task_params": [], + "connector_token": [ + "connectorId", + "tokenType" + ], + "query": [ + "description", + "title" + ], + "kql-telemetry": [], + "search-session": [ + "created", + "realmName", + "realmType", + "sessionId", + "username" + ], + "search-telemetry": [], + "file-upload-usage-collection-telemetry": [ + "file_upload", + "file_upload.index_creation_count" + ], + "apm-indices": [], + "tag": [ + "color", + "description", + "name" + ], + "alert": [ + "actions", + "actions.actionRef", + "actions.actionTypeId", + "actions.group", + "alertTypeId", + "consumer", + "createdAt", + "createdBy", + "enabled", + "executionStatus", + "executionStatus.error", + "executionStatus.error.message", + "executionStatus.error.reason", + "executionStatus.lastDuration", + "executionStatus.lastExecutionDate", + "executionStatus.numberOfTriggeredActions", + "executionStatus.status", + "executionStatus.warning", + "executionStatus.warning.message", + "executionStatus.warning.reason", + "lastRun", + "lastRun.alertsCount", + "lastRun.alertsCount.active", + "lastRun.alertsCount.ignored", + "lastRun.alertsCount.new", + "lastRun.alertsCount.recovered", + "lastRun.outcome", + "lastRun.outcomeOrder", + "legacyId", + "mapped_params", + "mapped_params.risk_score", + "mapped_params.severity", + "monitoring", + "monitoring.run", + "monitoring.run.calculated_metrics", + "monitoring.run.calculated_metrics.p50", + "monitoring.run.calculated_metrics.p95", + "monitoring.run.calculated_metrics.p99", + "monitoring.run.calculated_metrics.success_ratio", + "monitoring.run.last_run", + "monitoring.run.last_run.metrics", + "monitoring.run.last_run.metrics.duration", + "monitoring.run.last_run.metrics.gap_duration_s", + "monitoring.run.last_run.metrics.total_alerts_created", + "monitoring.run.last_run.metrics.total_alerts_detected", + "monitoring.run.last_run.metrics.total_indexing_duration_ms", + "monitoring.run.last_run.metrics.total_search_duration_ms", + "monitoring.run.last_run.timestamp", + "muteAll", + "mutedInstanceIds", + "name", + "notifyWhen", + "params", + "revision", + "running", + "schedule", + "schedule.interval", + "scheduledTaskId", + "snoozeSchedule", + "snoozeSchedule.duration", + "snoozeSchedule.id", + "snoozeSchedule.skipRecurrences", + "tags", + "throttle", + "updatedAt", + "updatedBy" + ], + "api_key_pending_invalidation": [ + "apiKeyId", + "createdAt" + ], + "rules-settings": [ + "flapping" + ], + "maintenance-window": [ + "enabled", + "events" + ], + "graph-workspace": [ + "description", + "kibanaSavedObjectMeta", + "kibanaSavedObjectMeta.searchSourceJSON", + "legacyIndexPatternRef", + "numLinks", + "numVertices", + "title", + "version", + "wsState" + ], + "search": [ + "description", + "title" + ], + "visualization": [ + "description", + "kibanaSavedObjectMeta", + "title", + "version" + ], + "canvas-element": [ + "@created", + "@timestamp", + "content", + "help", + "image", + "name" + ], + "canvas-workpad": [ + "@created", + "@timestamp", + "name" + ], + "canvas-workpad-template": [ + "help", + "name", + "tags", + "template_key" + ], + "event-annotation-group": [ + "description", + "title" + ], + "dashboard": [ + "controlGroupInput", + "controlGroupInput.chainingSystem", + "controlGroupInput.controlStyle", + "controlGroupInput.ignoreParentSettingsJSON", + "controlGroupInput.panelsJSON", + "description", + "hits", + "kibanaSavedObjectMeta", + "kibanaSavedObjectMeta.searchSourceJSON", + "optionsJSON", + "panelsJSON", + "refreshInterval", + "refreshInterval.display", + "refreshInterval.pause", + "refreshInterval.section", + "refreshInterval.value", + "timeFrom", + "timeRestore", + "timeTo", + "title", + "version" + ], + "links": [ + "description", + "links", + "title" + ], + "lens": [ + "description", + "state", + "title", + "visualizationType" + ], + "lens-ui-telemetry": [ + "count", + "date", + "name", + "type" + ], + "map": [ + "bounds", + "description", + "layerListJSON", + "mapStateJSON", + "title", + "uiStateJSON", + "version" + ], + "cases-comments": [ + "actions", + "actions.type", + "alertId", + "comment", + "created_at", + "created_by", + "created_by.username", + "externalReferenceAttachmentTypeId", + "owner", + "persistableStateAttachmentTypeId", + "pushed_at", + "type", + "updated_at" + ], + "cases-configure": [ + "closure_type", + "created_at", + "owner" + ], + "cases-connector-mappings": [ + "owner" + ], + "cases": [ + "assignees", + "assignees.uid", + "category", + "closed_at", + "closed_by", + "closed_by.email", + "closed_by.full_name", + "closed_by.profile_uid", + "closed_by.username", + "connector", + "connector.fields", + "connector.fields.key", + "connector.fields.value", + "connector.name", + "connector.type", + "created_at", + "created_by", + "created_by.email", + "created_by.full_name", + "created_by.profile_uid", + "created_by.username", + "customFields", + "customFields.key", + "customFields.type", + "customFields.value", + "description", + "duration", + "external_service", + "external_service.connector_name", + "external_service.external_id", + "external_service.external_title", + "external_service.external_url", + "external_service.pushed_at", + "external_service.pushed_by", + "external_service.pushed_by.email", + "external_service.pushed_by.full_name", + "external_service.pushed_by.profile_uid", + "external_service.pushed_by.username", + "owner", + "settings", + "settings.syncAlerts", + "severity", + "status", + "tags", + "title", + "total_alerts", + "total_comments", + "updated_at", + "updated_by", + "updated_by.email", + "updated_by.full_name", + "updated_by.profile_uid", + "updated_by.username" + ], + "cases-user-actions": [ + "action", + "created_at", + "created_by", + "created_by.username", + "owner", + "payload", + "payload.assignees", + "payload.assignees.uid", + "payload.comment", + "payload.comment.externalReferenceAttachmentTypeId", + "payload.comment.persistableStateAttachmentTypeId", + "payload.comment.type", + "payload.connector", + "payload.connector.type", + "type" + ], + "cases-telemetry": [], + "infrastructure-monitoring-log-view": [ + "name" + ], + "metrics-data-source": [], + "ingest_manager_settings": [ + "fleet_server_hosts", + "has_seen_add_data_notice", + "prerelease_integrations_enabled", + "secret_storage_requirements_met" + ], + "ingest-agent-policies": [ + "agent_features", + "agent_features.enabled", + "agent_features.name", + "data_output_id", + "description", + "download_source_id", + "fleet_server_host_id", + "inactivity_timeout", + "is_default", + "is_default_fleet_server", + "is_managed", + "is_preconfigured", + "is_protected", + "keep_monitoring_alive", + "monitoring_enabled", + "monitoring_output_id", + "name", + "namespace", + "overrides", + "revision", + "schema_version", + "status", + "unenroll_timeout", + "updated_at", + "updated_by" + ], + "ingest-outputs": [ + "allow_edit", + "auth_type", + "broker_ack_reliability", + "broker_buffer_size", + "broker_timeout", + "ca_sha256", + "ca_trusted_fingerprint", + "channel_buffer_size", + "client_id", + "compression", + "compression_level", + "config", + "config_yaml", + "connection_type", + "hash", + "hash.hash", + "hash.random", + "headers", + "headers.key", + "headers.value", + "hosts", + "is_default", + "is_default_monitoring", + "is_preconfigured", + "key", + "name", + "output_id", + "partition", + "password", + "proxy_id", + "random", + "random.group_events", + "required_acks", + "round_robin", + "round_robin.group_events", + "sasl", + "sasl.mechanism", + "secrets", + "secrets.password", + "secrets.password.id", + "secrets.service_token", + "secrets.service_token.id", + "secrets.ssl", + "secrets.ssl.key", + "secrets.ssl.key.id", + "service_token", + "shipper", + "ssl", + "timeout", + "topics", + "topics.topic", + "topics.when", + "topics.when.condition", + "topics.when.type", + "type", + "username", + "version" + ], + "ingest-package-policies": [ + "created_at", + "created_by", + "description", + "elasticsearch", + "enabled", + "inputs", + "is_managed", + "name", + "namespace", + "package", + "package.name", + "package.title", + "package.version", + "policy_id", + "revision", + "secret_references", + "secret_references.id", + "updated_at", + "updated_by", + "vars" + ], + "epm-packages": [ + "es_index_patterns", + "experimental_data_stream_features", + "experimental_data_stream_features.data_stream", + "experimental_data_stream_features.features", + "experimental_data_stream_features.features.synthetic_source", + "experimental_data_stream_features.features.tsdb", + "install_format_schema_version", + "install_source", + "install_started_at", + "install_status", + "install_version", + "installed_es", + "installed_es.deferred", + "installed_es.id", + "installed_es.type", + "installed_es.version", + "installed_kibana", + "installed_kibana_space_id", + "internal", + "keep_policies_up_to_date", + "latest_install_failed_attempts", + "name", + "package_assets", + "verification_key_id", + "verification_status", + "version" + ], + "epm-packages-assets": [ + "asset_path", + "data_base64", + "data_utf8", + "install_source", + "media_type", + "package_name", + "package_version" + ], + "fleet-preconfiguration-deletion-record": [ + "id" + ], + "ingest-download-sources": [ + "host", + "is_default", + "name", + "proxy_id", + "source_id" + ], + "fleet-fleet-server-host": [ + "host_urls", + "is_default", + "is_preconfigured", + "name", + "proxy_id" + ], + "fleet-proxy": [ + "certificate", + "certificate_authorities", + "certificate_key", + "is_preconfigured", + "name", + "proxy_headers", + "url" + ], + "fleet-message-signing-keys": [], + "fleet-uninstall-tokens": [ + "policy_id", + "token_plain" + ], + "osquery-manager-usage-metric": [ + "count", + "errors" + ], + "osquery-saved-query": [ + "created_at", + "created_by", + "description", + "ecs_mapping", + "id", + "interval", + "platform", + "query", + "timeout", + "updated_at", + "updated_by", + "version" + ], + "osquery-pack": [ + "created_at", + "created_by", + "description", + "enabled", + "name", + "queries", + "queries.ecs_mapping", + "queries.id", + "queries.interval", + "queries.platform", + "queries.query", + "queries.timeout", + "queries.version", + "shards", + "updated_at", + "updated_by", + "version" + ], + "osquery-pack-asset": [ + "description", + "name", + "queries", + "queries.ecs_mapping", + "queries.id", + "queries.interval", + "queries.platform", + "queries.query", + "queries.timeout", + "queries.version", + "shards", + "version" + ], + "csp-rule-template": [ + "metadata", + "metadata.benchmark", + "metadata.benchmark.id", + "metadata.benchmark.name", + "metadata.benchmark.posture_type", + "metadata.benchmark.rule_number", + "metadata.benchmark.version", + "metadata.id", + "metadata.name", + "metadata.section", + "metadata.version" + ], + "slo": [ + "budgetingMethod", + "description", + "enabled", + "id", + "indicator", + "indicator.params", + "indicator.type", + "name", + "tags" + ], + "threshold-explorer-view": [], + "observability-onboarding-state": [ + "progress", + "state", + "type" + ], + "ml-job": [ + "datafeed_id", + "job_id", + "type" + ], + "ml-trained-model": [ + "job", + "job.create_time", + "job.job_id", + "model_id" + ], + "ml-module": [ + "datafeeds", + "defaultIndexPattern", + "description", + "id", + "jobs", + "logo", + "query", + "tags", + "title", + "type" + ], + "uptime-dynamic-settings": [], + "synthetics-privates-locations": [], + "synthetics-monitor": [ + "alert", + "alert.status", + "alert.status.enabled", + "alert.tls", + "alert.tls.enabled", + "custom_heartbeat_id", + "enabled", + "hash", + "hosts", + "id", + "journey_id", + "locations", + "locations.id", + "locations.label", + "name", + "origin", + "project_id", + "schedule", + "schedule.number", + "tags", + "throttling", + "throttling.label", + "type", + "urls" + ], + "uptime-synthetics-api-key": [ + "apiKey" + ], + "synthetics-param": [], + "infrastructure-ui-source": [], + "inventory-view": [], + "metrics-explorer-view": [], + "upgrade-assistant-reindex-operation": [ + "indexName", + "status" + ], + "upgrade-assistant-ml-upgrade-operation": [ + "snapshotId" + ], + "monitoring-telemetry": [ + "reportedClusterUuids" + ], + "enterprise_search_telemetry": [], + "app_search_telemetry": [], + "workplace_search_telemetry": [], + "siem-ui-timeline-note": [ + "created", + "createdBy", + "eventId", + "note", + "updated", + "updatedBy" + ], + "siem-ui-timeline-pinned-event": [ + "created", + "createdBy", + "eventId", + "updated", + "updatedBy" + ], + "siem-detection-engine-rule-actions": [ + "actions", + "actions.actionRef", + "actions.action_type_id", + "actions.group", + "actions.id", + "actions.params", + "alertThrottle", + "ruleAlertId", + "ruleThrottle" + ], + "security-rule": [ + "rule_id", + "version" + ], + "siem-ui-timeline": [ + "columns", + "columns.aggregatable", + "columns.category", + "columns.columnHeaderType", + "columns.description", + "columns.example", + "columns.id", + "columns.indexes", + "columns.name", + "columns.placeholder", + "columns.searchable", + "columns.type", + "created", + "createdBy", + "dataProviders", + "dataProviders.and", + "dataProviders.and.enabled", + "dataProviders.and.excluded", + "dataProviders.and.id", + "dataProviders.and.kqlQuery", + "dataProviders.and.name", + "dataProviders.and.queryMatch", + "dataProviders.and.queryMatch.displayField", + "dataProviders.and.queryMatch.displayValue", + "dataProviders.and.queryMatch.field", + "dataProviders.and.queryMatch.operator", + "dataProviders.and.queryMatch.value", + "dataProviders.and.type", + "dataProviders.enabled", + "dataProviders.excluded", + "dataProviders.id", + "dataProviders.kqlQuery", + "dataProviders.name", + "dataProviders.queryMatch", + "dataProviders.queryMatch.displayField", + "dataProviders.queryMatch.displayValue", + "dataProviders.queryMatch.field", + "dataProviders.queryMatch.operator", + "dataProviders.queryMatch.value", + "dataProviders.type", + "dateRange", + "dateRange.end", + "dateRange.start", + "description", + "eqlOptions", + "eqlOptions.eventCategoryField", + "eqlOptions.query", + "eqlOptions.size", + "eqlOptions.tiebreakerField", + "eqlOptions.timestampField", + "eventType", + "excludedRowRendererIds", + "favorite", + "favorite.favoriteDate", + "favorite.fullName", + "favorite.keySearch", + "favorite.userName", + "filters", + "filters.exists", + "filters.match_all", + "filters.meta", + "filters.meta.alias", + "filters.meta.controlledBy", + "filters.meta.disabled", + "filters.meta.field", + "filters.meta.formattedValue", + "filters.meta.index", + "filters.meta.key", + "filters.meta.negate", + "filters.meta.params", + "filters.meta.relation", + "filters.meta.type", + "filters.meta.value", + "filters.missing", + "filters.query", + "filters.range", + "filters.script", + "indexNames", + "kqlMode", + "kqlQuery", + "kqlQuery.filterQuery", + "kqlQuery.filterQuery.kuery", + "kqlQuery.filterQuery.kuery.expression", + "kqlQuery.filterQuery.kuery.kind", + "kqlQuery.filterQuery.serializedQuery", + "savedSearchId", + "sort", + "sort.columnId", + "sort.columnType", + "sort.sortDirection", + "status", + "templateTimelineId", + "templateTimelineVersion", + "timelineType", + "title", + "updated", + "updatedBy" + ], + "endpoint:user-artifact-manifest": [ + "artifacts", + "schemaVersion" + ], + "security-solution-signals-migration": [ + "sourceIndex", + "updated", + "version" + ], + "risk-engine-configuration": [ + "dataViewId", + "enabled", + "filter", + "identifierType", + "interval", + "pageSize", + "range", + "range.end", + "range.start" + ], + "policy-settings-protection-updates-note": [ + "note" + ], + "apm-telemetry": [], + "apm-server-schema": [ + "schemaJson" + ], + "apm-service-group": [ + "color", + "description", + "groupName", + "kuery" + ], + "apm-custom-dashboards": [ + "dashboardSavedObjectId", + "kuery", + "serviceEnvironmentFilterEnabled", + "serviceNameFilterEnabled" + ] +} diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 603f0efd54a87..fcc8422c45865 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1844,6 +1844,14 @@ } } } + }, + "service_token": { + "dynamic": false, + "properties": { + "id": { + "type": "keyword" + } + } } } } @@ -1959,6 +1967,10 @@ } } }, + "latest_install_failed_attempts": { + "type": "object", + "enabled": false + }, "installed_kibana": { "dynamic": false, "properties": {} diff --git a/packages/kbn-check-mappings-update-cli/src/check_additive_only_change.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/check_additive_only_change.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/check_additive_only_change.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/check_additive_only_change.ts diff --git a/packages/kbn-check-mappings-update-cli/src/check_additve_only_change.test.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/check_additve_only_change.test.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/check_additve_only_change.test.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/check_additve_only_change.test.ts diff --git a/packages/kbn-check-mappings-update-cli/src/check_incompatible_mappings.test.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.test.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/check_incompatible_mappings.test.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.test.ts diff --git a/packages/kbn-check-mappings-update-cli/src/check_incompatible_mappings.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/check_incompatible_mappings.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts diff --git a/packages/kbn-check-mappings-update-cli/src/current_mappings.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/current_mappings.ts similarity index 97% rename from packages/kbn-check-mappings-update-cli/src/current_mappings.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/current_mappings.ts index c7e7dac4763a6..5632f3c479d18 100644 --- a/packages/kbn-check-mappings-update-cli/src/current_mappings.ts +++ b/packages/kbn-check-mappings-update-cli/src/compatibility/current_mappings.ts @@ -11,7 +11,7 @@ import Path from 'path'; import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects-base-server-internal'; -export const CURRENT_MAPPINGS_FILE = Path.resolve(__dirname, '../current_mappings.json'); +export const CURRENT_MAPPINGS_FILE = Path.resolve(__dirname, '../../current_mappings.json'); export async function readCurrentMappings(): Promise { let currentMappingsJson; diff --git a/packages/kbn-check-mappings-update-cli/src/extract_mappings_from_plugins.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/extract_mappings_from_plugins.ts similarity index 90% rename from packages/kbn-check-mappings-update-cli/src/extract_mappings_from_plugins.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/extract_mappings_from_plugins.ts index 2c2884b446338..420febc93c3bd 100644 --- a/packages/kbn-check-mappings-update-cli/src/extract_mappings_from_plugins.ts +++ b/packages/kbn-check-mappings-update-cli/src/compatibility/extract_mappings_from_plugins.ts @@ -8,7 +8,6 @@ import ChildProcess from 'child_process'; import { Readable } from 'stream'; - import * as Rx from 'rxjs'; import { REPO_ROOT } from '@kbn/repo-info'; @@ -18,19 +17,13 @@ import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects import type { Result } from './extract_mappings_from_plugins_worker'; -function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') { - return observeLines(readable).pipe( - Rx.tap((line) => { - log[level](line); - }), - Rx.ignoreElements() - ); -} - /** * Run a worker process that starts the core with all plugins enabled and sends back the - * saved object mappings for all plugins. We run this in a child process so that we can - * harvest logs and feed them into the logger when debugging. + * saved object mappings for all plugins. + * + * We run this in a child process to make it easier to kill the kibana instance once done + * (dodges issues with open handles), and so that we can harvest logs and feed them into + * the logger when debugging. */ export async function extractMappingsFromPlugins( log: SomeDevLog @@ -78,3 +71,12 @@ export async function extractMappingsFromPlugins( return mappings; } + +function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') { + return observeLines(readable).pipe( + Rx.tap((line) => { + log[level](line); + }), + Rx.ignoreElements() + ); +} diff --git a/packages/kbn-check-mappings-update-cli/src/extract_mappings_from_plugins_worker.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/extract_mappings_from_plugins_worker.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/extract_mappings_from_plugins_worker.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/extract_mappings_from_plugins_worker.ts diff --git a/packages/kbn-check-mappings-update-cli/src/compatibility/index.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/index.ts new file mode 100644 index 0000000000000..88869c8e449f6 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/compatibility/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 { runMappingsCompatibilityChecks } from './run_mappings_compatibility_check'; diff --git a/packages/kbn-check-mappings-update-cli/src/mocks.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/mocks.ts similarity index 100% rename from packages/kbn-check-mappings-update-cli/src/mocks.ts rename to packages/kbn-check-mappings-update-cli/src/compatibility/mocks.ts diff --git a/packages/kbn-check-mappings-update-cli/src/compatibility/run_mappings_compatibility_check.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/run_mappings_compatibility_check.ts new file mode 100644 index 0000000000000..38c339dd43e55 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/compatibility/run_mappings_compatibility_check.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 deepEqual from 'fast-deep-equal'; +import { ToolingLog } from '@kbn/tooling-log'; +import { CleanupTask } from '@kbn/dev-cli-runner'; +import { createTestEsCluster } from '@kbn/test'; +import { extractMappingsFromPlugins } from './extract_mappings_from_plugins'; +import { checkAdditiveOnlyChange } from './check_additive_only_change'; +import { checkIncompatibleMappings } from './check_incompatible_mappings'; +import { readCurrentMappings, updateCurrentMappings } from './current_mappings'; + +export const runMappingsCompatibilityChecks = async ({ + fix, + verify, + log, + addCleanupTask, +}: { + fix: boolean; + verify: boolean; + log: ToolingLog; + addCleanupTask: (task: CleanupTask) => void; +}) => { + /** + * Algorithm for checking compatible mappings. Should work in CI or local + * dev environment. + * 1. Extract mappings from code as JSON object + * 2. Check if extracted mappings is different from current_mappings.json, current_mappings.json stores + * the mappings from upstream and is commited to each branch + * 3. Start a fresh ES node + * 4. Upload current_mappings.json to ES node + * 5. Upload extracted mappings.json to ES node + * 6. Check result of response to step 5, if bad response the mappings are incompatible + * 7. If good response, write extracted mappings to current_mappings.json + */ + + log.info('Extracting mappings from plugins'); + const extractedMappings = await log.indent(4, async () => { + return await extractMappingsFromPlugins(log); + }); + + const currentMappings = await readCurrentMappings(); + const isMappingChanged = !deepEqual(currentMappings, extractedMappings); + + if (!isMappingChanged) { + log.success('Mappings are unchanged.'); + return; + } + + if (verify) { + log.info('Checking if any mappings have been removed'); + await log.indent(4, async () => { + return checkAdditiveOnlyChange(log, currentMappings, extractedMappings); + }); + + log.info('Starting es...'); + const esClient = await log.indent(4, async () => { + const cluster = createTestEsCluster({ log }); + await cluster.start(); + addCleanupTask(() => cluster.cleanup()); + return cluster.getClient(); + }); + + log.info(`Checking if mappings are compatible`); + await log.indent(4, async () => { + await checkIncompatibleMappings({ + log, + esClient, + currentMappings, + nextMappings: extractedMappings, + }); + }); + } + + if (fix) { + await updateCurrentMappings(extractedMappings); + log.warning( + `Updated extracted mappings in current_mappings.json file, please commit the changes if desired.` + ); + } else { + log.warning( + `The extracted mappings do not match the current_mappings.json file, run with --fix to update.` + ); + } +}; diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/compare_type_field_lists.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/compare_type_field_lists.ts new file mode 100644 index 0000000000000..b0eb2a6fd0135 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/compare_type_field_lists.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { difference } from 'lodash'; + +export interface CompareResult { + error: boolean; + fieldsToAdd: string[]; + registeredFields: string[]; + missingFromModelVersion: string[]; + missingFromDefinition: string[]; +} + +export const compareFieldLists = ({ + currentFields, + registeredFields = [], + modelVersionFields = [], +}: { + currentFields: string[] | undefined; + registeredFields: string[] | undefined; + modelVersionFields: string[] | undefined; +}): CompareResult => { + // type not present in the file, so it was just added. + // in that case we just update the file to add all the registered fields. + if (!currentFields) { + return { + error: false, + registeredFields, + fieldsToAdd: registeredFields, + missingFromModelVersion: [], + missingFromDefinition: [], + }; + } + + // we search all registered/mv fields not already in the file + const registeredFieldsNotInCurrent = difference(registeredFields, currentFields); + const modelVersionFieldsNotInCurrent = difference(modelVersionFields, currentFields); + + // then we search for registered fields not in model versions, and the opposite + const registeredFieldsNotInModelVersions = difference( + registeredFieldsNotInCurrent, + modelVersionFieldsNotInCurrent + ); + const modelVersionFieldsNotRegistered = difference( + modelVersionFieldsNotInCurrent, + registeredFieldsNotInCurrent + ); + + // if any non-file field is present only in mapping definition or in model version, then there's an error on the type + const anyFieldMissing = + registeredFieldsNotInModelVersions.length > 0 || modelVersionFieldsNotRegistered.length > 0; + + return { + error: anyFieldMissing, + registeredFields, + fieldsToAdd: registeredFieldsNotInCurrent, + missingFromModelVersion: registeredFieldsNotInModelVersions, + missingFromDefinition: modelVersionFieldsNotRegistered, + }; +}; diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/current_fields.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/current_fields.ts new file mode 100644 index 0000000000000..0029f4f40142b --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/current_fields.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { readFile, writeFile } from 'fs/promises'; +import Path from 'path'; +import { FieldListMap } from '@kbn/core-saved-objects-base-server-internal'; + +const CURRENT_FIELDS_FILE_PATH = Path.resolve(__dirname, '../../current_fields.json'); + +export const readCurrentFields = async (): Promise => { + try { + const fileContent = await readFile(CURRENT_FIELDS_FILE_PATH, 'utf-8'); + return JSON.parse(fileContent); + } catch (error) { + if (error.code === 'ENOENT') { + return {}; + } + throw error; + } +}; + +export const writeCurrentFields = async (fieldMap: FieldListMap) => { + await writeFile(CURRENT_FIELDS_FILE_PATH, JSON.stringify(fieldMap, null, 2) + '\n', 'utf-8'); +}; diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins.ts new file mode 100644 index 0000000000000..118559f63e311 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import ChildProcess from 'child_process'; +import { Readable } from 'stream'; +import * as Rx from 'rxjs'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { SomeDevLog } from '@kbn/some-dev-log'; +import { observeLines } from '@kbn/stdio-dev-helpers'; +import type { Result } from './extract_field_lists_from_plugins_worker'; + +/** + * Run a worker process that starts the core with all plugins enabled and sends back the + * registered fields for all plugins. + * + * We run this in a child process to make it easier to kill the kibana instance once done + * (dodges issues with open handles), and so that we can harvest logs and feed them into + * the logger when debugging. + */ +export async function extractFieldListsFromPlugins(log: SomeDevLog): Promise { + log.info('Loading core with all plugins enabled so that we can get all savedObject mappings...'); + + const fork = ChildProcess.fork(require.resolve('./extract_field_lists_from_plugins_worker.ts'), { + execArgv: ['--require=@kbn/babel-register/install'], + cwd: REPO_ROOT, + stdio: ['ignore', 'pipe', 'pipe', 'ipc'], + }); + + const result = await Rx.firstValueFrom( + Rx.merge( + // the actual value we are interested in + Rx.fromEvent(fork, 'message'), + + // worker logs are written to the logger, but dropped from the stream + routeToLog(fork.stdout!, log, 'debug'), + routeToLog(fork.stderr!, log, 'error'), + + // if an error occurs running the worker throw it into the stream + Rx.fromEvent(fork, 'error').pipe( + Rx.map((err) => { + throw err; + }) + ) + ).pipe( + Rx.takeUntil(Rx.fromEvent(fork, 'exit')), + Rx.map((results) => { + const [outcome] = results as [Result]; + log.debug('message received from worker', outcome); + fork.kill('SIGILL'); + return outcome; + }), + Rx.defaultIfEmpty(undefined) + ) + ); + + if (!result) { + throw new Error('worker exited without sending mappings'); + } + + return result; +} + +function routeToLog(readable: Readable, log: SomeDevLog, level: 'debug' | 'error') { + return observeLines(readable).pipe( + Rx.tap((line) => { + log[level](line); + }), + Rx.ignoreElements() + ); +} diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins_worker.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins_worker.ts new file mode 100644 index 0000000000000..7402e82e467c5 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/extract_field_lists_from_plugins_worker.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createRootWithCorePlugins } from '@kbn/core-test-helpers-kbn-server'; +import { set } from '@kbn/safer-lodash-set'; +import { PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH } from '@kbn/core-plugins-server-internal/src/constants'; +import { + FieldListMap, + getFieldListMapFromMappingDefinitions, + SavedObjectsTypeMappingDefinitions, +} from '@kbn/core-saved-objects-base-server-internal'; +import { getFieldListMapFromModelVersions } from './get_field_list_from_model_version'; + +export interface Result { + fieldsFromRegisteredTypes: FieldListMap; + fieldsFromModelVersions: FieldListMap; +} + +(async () => { + if (!process.send) { + throw new Error('worker must be run in a node.js fork'); + } + + const settings = { + logging: { + loggers: [{ name: 'root', level: 'info', appenders: ['console'] }], + }, + migrations: { skip: true }, + elasticsearch: { skipStartupConnectionCheck: true }, + }; + + set(settings, PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH, true); + + const root = createRootWithCorePlugins(settings, { + basePath: false, + cache: false, + dev: true, + disableOptimizer: true, + silent: false, + dist: false, + oss: false, + runExamples: false, + watch: false, + }); + + await root.preboot(); + const { savedObjects } = await root.setup(); + const typeRegistry = savedObjects.getTypeRegistry(); + + const registeredTypes = typeRegistry.getAllTypes(); + const registeredMappings = registeredTypes.reduce( + (memo, type) => { + memo[type.name] = type.mappings; + return memo; + }, + {} + ); + + const fieldsFromRegisteredTypes = getFieldListMapFromMappingDefinitions(registeredMappings); + const fieldsFromModelVersions = getFieldListMapFromModelVersions(registeredTypes); + + const result: Result = { + fieldsFromRegisteredTypes, + fieldsFromModelVersions, + }; + process.send(result); +})().catch((error) => { + process.stderr.write(`UNHANDLED ERROR: ${error.stack}`); + process.exit(1); +}); diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/get_field_list_from_model_version.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/get_field_list_from_model_version.ts new file mode 100644 index 0000000000000..278d950dcd3f6 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/get_field_list_from_model_version.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { FieldListMap, getVersionAddedFields } from '@kbn/core-saved-objects-base-server-internal'; + +const getModelVersionAddedFieldsForType = (typeDef: SavedObjectsType): string[] => { + const addedFieldSet = new Set(); + const versions = + typeof typeDef.modelVersions === 'function' + ? typeDef.modelVersions() + : typeDef.modelVersions ?? {}; + Object.values(versions).forEach((version) => { + const addedFields = getVersionAddedFields(version); + addedFields.forEach((field) => addedFieldSet.add(field)); + }); + return [...addedFieldSet].sort(); +}; + +export const getFieldListMapFromModelVersions = (types: SavedObjectsType[]): FieldListMap => { + return types.reduce((memo, type) => { + memo[type.name] = getModelVersionAddedFieldsForType(type); + return memo; + }, {}); +}; diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/index.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/index.ts new file mode 100644 index 0000000000000..1b63ee3d07137 --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/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 { runModelVersionMappingAdditionsChecks } from './run_versions_mapping_additions_check'; diff --git a/packages/kbn-check-mappings-update-cli/src/mappings_additions/run_versions_mapping_additions_check.ts b/packages/kbn-check-mappings-update-cli/src/mappings_additions/run_versions_mapping_additions_check.ts new file mode 100644 index 0000000000000..b4bcc332f2dcb --- /dev/null +++ b/packages/kbn-check-mappings-update-cli/src/mappings_additions/run_versions_mapping_additions_check.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 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 { ToolingLog } from '@kbn/tooling-log'; +import { createFailError } from '@kbn/dev-cli-errors'; +import { FieldListMap } from '@kbn/core-saved-objects-base-server-internal'; +import { compareFieldLists, type CompareResult } from './compare_type_field_lists'; +import { readCurrentFields, writeCurrentFields } from './current_fields'; +import { extractFieldListsFromPlugins } from './extract_field_lists_from_plugins'; + +export const runModelVersionMappingAdditionsChecks = async ({ + fix, + override, + verify, + log, +}: { + fix: boolean; + override: boolean; + verify: boolean; + log: ToolingLog; +}) => { + log.info('Generating field lists from registry and file'); + const { fieldsFromRegisteredTypes, fieldsFromModelVersions } = await extractFieldListsFromPlugins( + log + ); + const currentFields = await readCurrentFields(); + + const allTypeNames = [ + ...new Set([ + ...Object.keys(fieldsFromRegisteredTypes), + ...Object.keys(currentFields), + ...Object.keys(fieldsFromModelVersions), + ]), + ]; + + log.info('Generating field delta'); + const results = allTypeNames.reduce>((memo, typeName) => { + memo[typeName] = compareFieldLists({ + registeredFields: fieldsFromRegisteredTypes[typeName], + currentFields: currentFields[typeName], + modelVersionFields: fieldsFromModelVersions[typeName], + }); + return memo; + }, {}); + + const hasError = Object.values(results).some((result) => result.error); + if (hasError) { + const errorMessage = getErrorMessage(results); + if (verify) { + throw createFailError(errorMessage + `\nUse --override --no-verify`); + } else { + log.warning(errorMessage); + } + } + + if (fix || override) { + log.info(`Updating field file with override: ${override}`); + const updatedFields = updateCurrentFields(currentFields, results, override); + await writeCurrentFields(updatedFields); + } +}; + +const getErrorMessage = (results: Record): string => { + const errors = Object.entries(results) + .filter(([_, result]) => result.error) + .reduce((memo, [typeName, result]) => { + if (result.missingFromDefinition.length) { + memo.push( + `- ${typeName}: found mappings from model version not present in mappings definition: ${result.missingFromDefinition.join( + ',' + )}` + ); + } + if (result.missingFromModelVersion.length) { + memo.push( + `- ${typeName}: found mappings from root definition not present in any model version: ${result.missingFromModelVersion.join( + ',' + )}` + ); + } + return memo; + }, []); + + return `Found issues in savedObjects mappings:\n${errors.join('\n')}`; +}; + +const updateCurrentFields = ( + currentFields: FieldListMap, + results: Record, + override: boolean +): FieldListMap => { + // mutating the field lists is fine + const updatedFields = override ? {} : { ...currentFields }; + Object.entries(results).forEach(([typeName, typeResult]) => { + if (override) { + updatedFields[typeName] = [...typeResult.registeredFields].sort(); + } else { + if (!typeResult.error) { + updatedFields[typeName] = [ + ...new Set([...(updatedFields[typeName] || []), ...typeResult.fieldsToAdd]), + ].sort(); + } + } + }); + return updatedFields; +}; diff --git a/packages/kbn-check-mappings-update-cli/src/run_check_mappings_update_cli.ts b/packages/kbn-check-mappings-update-cli/src/run_check_mappings_update_cli.ts index b1674211d61fb..8ccc757324f93 100644 --- a/packages/kbn-check-mappings-update-cli/src/run_check_mappings_update_cli.ts +++ b/packages/kbn-check-mappings-update-cli/src/run_check_mappings_update_cli.ts @@ -6,95 +6,46 @@ * Side Public License, v 1. */ -import deepEqual from 'fast-deep-equal'; import { run } from '@kbn/dev-cli-runner'; -import { createTestEsCluster } from '@kbn/test'; - -import { extractMappingsFromPlugins } from './extract_mappings_from_plugins'; - -import { checkAdditiveOnlyChange } from './check_additive_only_change'; -import { checkIncompatibleMappings } from './check_incompatible_mappings'; -import { readCurrentMappings, updateCurrentMappings } from './current_mappings'; +import { runMappingsCompatibilityChecks } from './compatibility'; +import { runModelVersionMappingAdditionsChecks } from './mappings_additions'; run( async ({ log, flagsReader, addCleanupTask }) => { const fix = flagsReader.boolean('fix'); const verify = flagsReader.boolean('verify'); + const override = flagsReader.boolean('override'); + const task = flagsReader.string('task'); - /** - * Algorithm for checking compatible mappings. Should work in CI or local - * dev environment. - * 1. Extract mappings from code as JSON object - * 2. Check if extracted mappings is different from current_mappings.json, current_mappings.json stores - * the mappings from upstream and is commited to each branch - * 3. Start a fresh ES node - * 4. Upload current_mappings.json to ES node - * 5. Upload extracted mappings.json to ES node - * 6. Check result of response to step 5, if bad response the mappings are incompatible - * 7. If good response, write extracted mappings to current_mappings.json - */ - - log.info('Extracting mappings from plugins'); - const extractedMappings = await log.indent(4, async () => { - return await extractMappingsFromPlugins(log); - }); - - const currentMappings = await readCurrentMappings(); - const isMappingChanged = !deepEqual(currentMappings, extractedMappings); - - if (!isMappingChanged) { - log.success('Mappings are unchanged.'); - return; - } - - if (verify) { - log.info('Checking if any mappings have been removed'); + if (!task || task === 'mapping-addition') { + log.info('Running model version mapping addition checks'); await log.indent(4, async () => { - return checkAdditiveOnlyChange(log, currentMappings, extractedMappings); + await runModelVersionMappingAdditionsChecks({ fix, override, verify, log }); }); - - log.info('Starting es...'); - const esClient = await log.indent(4, async () => { - const cluster = createTestEsCluster({ log }); - await cluster.start(); - addCleanupTask(() => cluster.cleanup()); - return cluster.getClient(); - }); - - log.info(`Checking if mappings are compatible`); + } + if (!task || task === 'compatibility') { + log.info('Running mapping compatibility checks'); await log.indent(4, async () => { - await checkIncompatibleMappings({ - log, - esClient, - currentMappings, - nextMappings: extractedMappings, - }); + await runMappingsCompatibilityChecks({ fix, verify, log, addCleanupTask }); }); } - - if (fix) { - await updateCurrentMappings(extractedMappings); - log.warning( - `Updated extracted mappings in current_mappings.json file, please commit the changes if desired.` - ); - } else { - log.warning( - `The extracted mappings do not match the current_mappings.json file, run with --fix to update.` - ); - } }, { description: ` - Determine if the current SavedObject mappings in the source code can be applied to the current mappings from upstream. + Determine if the changes performed to the savedObjects mappings are following our standards `, flags: { - boolean: ['fix', 'verify'], + boolean: ['fix', 'override', 'verify'], + string: ['task'], default: { verify: true, + mappings: true, }, help: ` --fix If the current mappings differ from the mappings in the file, update the current_mappings.json file + --override If the current mappings differ from the mappings in the file, update the current_mappings.json file --no-verify Don't run any validation, just update the current_mappings.json file. + --task Specify which task(s) to run (compatibility | mapping-addition) `, }, } diff --git a/packages/kbn-check-mappings-update-cli/tsconfig.json b/packages/kbn-check-mappings-update-cli/tsconfig.json index 1439669b1e1dd..48973be21d219 100644 --- a/packages/kbn-check-mappings-update-cli/tsconfig.json +++ b/packages/kbn-check-mappings-update-cli/tsconfig.json @@ -26,5 +26,7 @@ "@kbn/test", "@kbn/core-elasticsearch-client-server-mocks", "@kbn/safer-lodash-set", + "@kbn/tooling-log", + "@kbn/core-saved-objects-server", ] } diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index 434534f6d888a..b03d407c2fcc0 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -131,6 +131,23 @@ test("does not push new configs when reloading if config at path hasn't changed" expect(valuesReceived).toEqual(['value']); }); +test("does push new configs when reloading when config at path hasn't changed if ignoreUnchanged is false", async () => { + const rawConfig$ = new BehaviorSubject>({ key: 'value' }); + const rawConfigProvider = createRawConfigServiceMock({ rawConfig$ }); + + const configService = new ConfigService(rawConfigProvider, defaultEnv, logger); + await configService.setSchema('key', schema.string()); + + const valuesReceived: any[] = []; + configService.atPath('key', { ignoreUnchanged: false }).subscribe((value) => { + valuesReceived.push(value); + }); + + rawConfig$.next({ key: 'value' }); + + expect(valuesReceived).toEqual(['value', 'value']); +}); + test('pushes new config when reloading and config at path has changed', async () => { const rawConfig$ = new BehaviorSubject>({ key: 'value' }); const rawConfigProvider = createRawConfigServiceMock({ rawConfig$ }); diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index 0026876f70b4d..e1c4ccfb55fbe 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -10,7 +10,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { SchemaTypeError, Type, ValidationError } from '@kbn/config-schema'; import { cloneDeep, isEqual, merge } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; -import { BehaviorSubject, combineLatest, firstValueFrom, Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest, firstValueFrom, Observable, identity } from 'rxjs'; import { distinctUntilChanged, first, map, shareReplay, tap } from 'rxjs/operators'; import { Logger, LoggerFactory } from '@kbn/logging'; import { getDocLinks, DocLinks } from '@kbn/doc-links'; @@ -159,9 +159,13 @@ export class ConfigService { * against its registered schema. * * @param path - The path to the desired subset of the config. + * @param ignoreUnchanged - If true (default), will not emit if the config at path did not change. */ - public atPath(path: ConfigPath) { - return this.getValidatedConfigAtPath$(path) as Observable; + public atPath( + path: ConfigPath, + { ignoreUnchanged = true }: { ignoreUnchanged?: boolean } = {} + ) { + return this.getValidatedConfigAtPath$(path, { ignoreUnchanged }) as Observable; } /** @@ -310,10 +314,13 @@ export class ConfigService { ); } - private getValidatedConfigAtPath$(path: ConfigPath) { + private getValidatedConfigAtPath$( + path: ConfigPath, + { ignoreUnchanged = true }: { ignoreUnchanged?: boolean } = {} + ) { return this.config$.pipe( map((config) => config.get(path)), - distinctUntilChanged(isEqual), + ignoreUnchanged ? distinctUntilChanged(isEqual) : identity, map((config) => this.validateAtPath(path, config)) ); } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index afc8252e7e13e..8234daa4b4454 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -187,7 +187,6 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { machineLearningStart: `${ELASTICSEARCH_DOCS}nlp-example.html`, mailService: `${ENTERPRISE_SEARCH_DOCS}mailer-configuration.html`, mlDocumentEnrichment: `${ELASTICSEARCH_DOCS}ingest-pipeline-search-inference.html`, - mlDocumentEnrichmentUpdateMappings: `${ELASTICSEARCH_DOCS}ingest-pipeline-search-inference.html#ingest-pipeline-search-inference-update-mapping`, searchApplicationsTemplates: `${ELASTICSEARCH_DOCS}search-application-api.html`, searchApplicationsSearchApi: `${ELASTICSEARCH_DOCS}search-application-security.html`, searchApplications: `${ELASTICSEARCH_DOCS}search-application-overview.html`, @@ -459,6 +458,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { privileges: `${SECURITY_SOLUTION_DOCS}endpoint-management-req.html`, manageDetectionRules: `${SECURITY_SOLUTION_DOCS}rules-ui-management.html`, createEsqlRuleType: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#create-esql-rule`, + entityAnalytics: { + riskScorePrerequisites: `${SECURITY_SOLUTION_DOCS}ers-requirements.html`, + }, }, query: { eql: `${ELASTICSEARCH_DOCS}eql.html`, @@ -518,6 +520,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { trainedModels: `${MACHINE_LEARNING_DOCS}ml-trained-models.html`, startTrainedModelsDeployment: `${MACHINE_LEARNING_DOCS}ml-nlp-deploy-model.html`, nlpElser: `${MACHINE_LEARNING_DOCS}ml-nlp-elser.html`, + nlpImportModel: `${MACHINE_LEARNING_DOCS}ml-nlp-import-model.html`, }, transforms: { guide: `${ELASTICSEARCH_DOCS}transforms.html`, @@ -770,6 +773,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { installAndUninstallIntegrationAssets: `${FLEET_DOCS}install-uninstall-integration-assets.html`, elasticAgentInputConfiguration: `${FLEET_DOCS}elastic-agent-input-configuration.html`, policySecrets: `${FLEET_DOCS}agent-policy.html#agent-policy-secret-values`, + remoteESOoutput: `${FLEET_DOCS}monitor-elastic-agent.html#external-elasticsearch-monitoring`, }, ecs: { guide: `${ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`, @@ -812,6 +816,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { rubyOverview: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/ruby-api/${DOC_LINK_VERSION}/ruby_client.html`, rustGuide: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/rust-api/${DOC_LINK_VERSION}/index.html`, rustOverview: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/rust-api/${DOC_LINK_VERSION}/overview.html`, + eland: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/eland/${DOC_LINK_VERSION}/index.html`, }, endpoints: { troubleshooting: `${SECURITY_SOLUTION_DOCS}ts-management.html#ts-endpoints`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 5e4202dd7fa45..b2298eecd3e17 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -168,7 +168,6 @@ export interface DocLinks { readonly machineLearningStart: string; readonly mailService: string; readonly mlDocumentEnrichment: string; - readonly mlDocumentEnrichmentUpdateMappings: string; readonly searchApplicationsTemplates: string; readonly searchApplicationsSearchApi: string; readonly searchApplications: string; @@ -350,6 +349,9 @@ export interface DocLinks { readonly privileges: string; readonly manageDetectionRules: string; readonly createEsqlRuleType: string; + readonly entityAnalytics: { + readonly riskScorePrerequisites: string; + }; }; readonly query: { readonly eql: string; @@ -528,6 +530,7 @@ export interface DocLinks { installAndUninstallIntegrationAssets: string; elasticAgentInputConfiguration: string; policySecrets: string; + remoteESOoutput: string; }>; readonly ecs: { readonly guide: string; @@ -569,6 +572,7 @@ export interface DocLinks { readonly rubyOverview: string; readonly rustGuide: string; readonly rustOverview: string; + readonly eland: string; }; readonly endpoints: { readonly troubleshooting: string; diff --git a/packages/kbn-es-query/index.ts b/packages/kbn-es-query/index.ts index a14724fc3a748..ac0a078e34d94 100644 --- a/packages/kbn-es-query/index.ts +++ b/packages/kbn-es-query/index.ts @@ -56,6 +56,7 @@ export { getIndexPatternFromSQLQuery, getIndexPatternFromESQLQuery, getLanguageDisplayName, + cleanupESQLQueryForLensSuggestions, } from './src/es_query'; export { diff --git a/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts b/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts index ca98864aab446..504e6a5c93d44 100644 --- a/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts +++ b/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts @@ -12,6 +12,7 @@ import { getAggregateQueryMode, getIndexPatternFromSQLQuery, getIndexPatternFromESQLQuery, + cleanupESQLQueryForLensSuggestions, } from './es_aggregate_query'; describe('sql query helpers', () => { @@ -115,4 +116,18 @@ describe('sql query helpers', () => { expect(idxPattern9).toBe('foo-1, foo-2'); }); }); + + describe('cleanupESQLQueryForLensSuggestions', () => { + it('should not remove anything if a drop command is not present', () => { + expect(cleanupESQLQueryForLensSuggestions('from a | eval b = 1')).toBe('from a | eval b = 1'); + }); + + it('should remove multiple drop statement if present', () => { + expect( + cleanupESQLQueryForLensSuggestions( + 'from a | drop @timestamp | drop a | drop b | keep c | drop d' + ) + ).toBe('from a | keep c '); + }); + }); }); diff --git a/packages/kbn-es-query/src/es_query/es_aggregate_query.ts b/packages/kbn-es-query/src/es_query/es_aggregate_query.ts index 27a5e790569c8..f746505896360 100644 --- a/packages/kbn-es-query/src/es_query/es_aggregate_query.ts +++ b/packages/kbn-es-query/src/es_query/es_aggregate_query.ts @@ -66,3 +66,8 @@ export function getIndexPatternFromESQLQuery(esql?: string): string { } return ''; } + +export function cleanupESQLQueryForLensSuggestions(esql?: string): string { + const pipes = (esql || '').split('|'); + return pipes.filter((statement) => !/DROP\s/i.test(statement)).join('|'); +} diff --git a/packages/kbn-es-query/src/es_query/index.ts b/packages/kbn-es-query/src/es_query/index.ts index 22141a52e93f5..18009145a432f 100644 --- a/packages/kbn-es-query/src/es_query/index.ts +++ b/packages/kbn-es-query/src/es_query/index.ts @@ -20,6 +20,7 @@ export { getIndexPatternFromSQLQuery, getLanguageDisplayName, getIndexPatternFromESQLQuery, + cleanupESQLQueryForLensSuggestions, } from './es_aggregate_query'; export { fromCombinedFilter } from './from_combined_filter'; export type { diff --git a/packages/kbn-es/src/cli_commands/serverless.ts b/packages/kbn-es/src/cli_commands/serverless.ts index 87a573a8810dd..0743cf2e7b5b6 100644 --- a/packages/kbn-es/src/cli_commands/serverless.ts +++ b/packages/kbn-es/src/cli_commands/serverless.ts @@ -38,6 +38,7 @@ export const serverless: Command = { --host Publish ES docker container on additional host IP --port The port to bind to on 127.0.0.1 [default: ${DEFAULT_PORT}] --ssl Enable HTTP SSL on the ES cluster + --kibanaUrl Fully qualified URL where Kibana is hosted (including base path). [default: https://localhost:5601/] --skipTeardown If this process exits, leave the ES cluster running in the background --waitForReady Wait for the ES cluster to be ready to serve requests --resources Overrides resources under ES 'config/' directory, which are by default @@ -73,7 +74,7 @@ export const serverless: Command = { files: 'F', }, - string: ['tag', 'image', 'basePath', 'resources', 'host'], + string: ['tag', 'image', 'basePath', 'resources', 'host', 'kibanaUrl'], boolean: ['clean', 'ssl', 'kill', 'background', 'skipTeardown', 'waitForReady'], default: defaults, diff --git a/packages/kbn-es/src/paths.ts b/packages/kbn-es/src/paths.ts index d9b4be41aa15b..4da4448573384 100644 --- a/packages/kbn-es/src/paths.ts +++ b/packages/kbn-es/src/paths.ts @@ -8,6 +8,7 @@ import Os from 'os'; import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; function maybeUseBat(bin: string) { return Os.platform().startsWith('win') ? `${bin}.bat` : bin; @@ -51,6 +52,8 @@ export const SERVERLESS_SECRETS_SSL_PATH = resolve( export const SERVERLESS_JWKS_PATH = resolve(__dirname, './serverless_resources/jwks.json'); +export const SERVERLESS_IDP_METADATA_PATH = resolve(REPO_ROOT, '.es', 'idp_metadata.xml'); + export const SERVERLESS_RESOURCES_PATHS = [ SERVERLESS_OPERATOR_USERS_PATH, SERVERLESS_ROLE_MAPPING_PATH, diff --git a/packages/kbn-es/src/serverless_resources/roles.yml b/packages/kbn-es/src/serverless_resources/roles.yml index 5f968ece56e3a..a98652d5b9311 100644 --- a/packages/kbn-es/src/serverless_resources/roles.yml +++ b/packages/kbn-es/src/serverless_resources/roles.yml @@ -210,6 +210,7 @@ t3_analyst: privileges: - read - write + - maintenance - names: - .lists* - .items* @@ -263,12 +264,16 @@ threat_intelligence_analyst: - endgame-* - filebeat-* - logs-* - - .lists* - - .items* - packetbeat-* - winlogbeat-* privileges: - read + - names: + - .lists* + - .items* + privileges: + - read + - write - names: - .alerts-security* - .siem-signals-* @@ -287,8 +292,7 @@ threat_intelligence_analyst: - application: "kibana-.kibana" privileges: - feature_ml.read - - feature_siem.read - - feature_siem.read_alerts + - feature_siem.all - feature_siem.endpoint_list_read - feature_siem.blocklist_all - feature_securitySolutionCases.all @@ -573,12 +577,12 @@ endpoint_operations_analyst: privileges: - read - write + - maintenance applications: - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all - - feature_siem.read_alerts - feature_siem.policy_management_all - feature_siem.endpoint_list_all - feature_siem.trusted_applications_all @@ -623,11 +627,15 @@ endpoint_policy_manager: - logs-* - packetbeat-* - winlogbeat-* + - risk-score.risk-score-* + privileges: + - read + - names: - .lists* - .items* - - risk-score.risk-score-* privileges: - read + - write - names: - .alerts-security* - .siem-signals-* diff --git a/packages/kbn-es/src/serverless_resources/security_roles.json b/packages/kbn-es/src/serverless_resources/security_roles.json index 5ac286a41c164..c4a06751e19b7 100644 --- a/packages/kbn-es/src/serverless_resources/security_roles.json +++ b/packages/kbn-es/src/serverless_resources/security_roles.json @@ -107,7 +107,7 @@ }, { "names": [".alerts-security*", ".siem-signals-*"], - "privileges": ["read", "write"] + "privileges": ["read", "write", "maintenance"] }, { "names": [".lists*", ".items*"], diff --git a/packages/kbn-es/src/utils/docker.test.ts b/packages/kbn-es/src/utils/docker.test.ts index 2d71a4e628e11..b574447a20508 100644 --- a/packages/kbn-es/src/utils/docker.test.ts +++ b/packages/kbn-es/src/utils/docker.test.ts @@ -32,15 +32,17 @@ import { ServerlessOptions, } from './docker'; import { ToolingLog, ToolingLogCollectingWriter } from '@kbn/tooling-log'; -import { ES_P12_PATH } from '@kbn/dev-utils'; +import { CA_CERT_PATH, ES_P12_PATH } from '@kbn/dev-utils'; import { SERVERLESS_CONFIG_PATH, SERVERLESS_RESOURCES_PATHS, SERVERLESS_SECRETS_PATH, SERVERLESS_JWKS_PATH, + SERVERLESS_IDP_METADATA_PATH, } from '../paths'; import * as waitClusterUtil from './wait_until_cluster_ready'; import * as waitForSecurityIndexUtil from './wait_for_security_index'; +import * as mockIdpPluginUtil from '@kbn/mock-idp-plugin/common'; jest.mock('execa'); const execa = jest.requireMock('execa'); @@ -58,6 +60,8 @@ jest.mock('./wait_for_security_index', () => ({ waitForSecurityIndex: jest.fn(), })); +jest.mock('@kbn/mock-idp-plugin/common'); + const log = new ToolingLog(); const logWriter = new ToolingLogCollectingWriter(); log.setWriters([logWriter]); @@ -69,6 +73,8 @@ const serverlessObjectStorePath = `${baseEsPath}/${serverlessDir}`; const waitUntilClusterReadyMock = jest.spyOn(waitClusterUtil, 'waitUntilClusterReady'); const waitForSecurityIndexMock = jest.spyOn(waitForSecurityIndexUtil, 'waitForSecurityIndex'); +const ensureSAMLRoleMappingMock = jest.spyOn(mockIdpPluginUtil, 'ensureSAMLRoleMapping'); +const createMockIdpMetadataMock = jest.spyOn(mockIdpPluginUtil, 'createMockIdpMetadata'); beforeEach(() => { jest.resetAllMocks(); @@ -423,6 +429,66 @@ describe('resolveEsArgs()', () => { ] `); }); + + test('should add SAML realm args when kibanaUrl and SSL are passed', () => { + const esArgs = resolveEsArgs([], { + ssl: true, + kibanaUrl: 'https://localhost:5601/', + }); + + expect(esArgs).toHaveLength(26); + expect(esArgs).toMatchInlineSnapshot(` + Array [ + "--env", + "xpack.security.http.ssl.enabled=true", + "--env", + "xpack.security.http.ssl.keystore.path=/usr/share/elasticsearch/config/certs/elasticsearch.p12", + "--env", + "xpack.security.http.ssl.verification_mode=certificate", + "--env", + "xpack.security.authc.realms.saml.mock-idp.order=0", + "--env", + "xpack.security.authc.realms.saml.mock-idp.idp.metadata.path=/usr/share/elasticsearch/config/secrets/idp_metadata.xml", + "--env", + "xpack.security.authc.realms.saml.mock-idp.idp.entity_id=urn:mock-idp", + "--env", + "xpack.security.authc.realms.saml.mock-idp.sp.entity_id=https://localhost:5601", + "--env", + "xpack.security.authc.realms.saml.mock-idp.sp.acs=https://localhost:5601/api/security/saml/callback", + "--env", + "xpack.security.authc.realms.saml.mock-idp.sp.logout=https://localhost:5601/logout", + "--env", + "xpack.security.authc.realms.saml.mock-idp.attributes.principal=http://saml.elastic-cloud.com/attributes/principal", + "--env", + "xpack.security.authc.realms.saml.mock-idp.attributes.groups=http://saml.elastic-cloud.com/attributes/roles", + "--env", + "xpack.security.authc.realms.saml.mock-idp.attributes.name=http://saml.elastic-cloud.com/attributes/email", + "--env", + "xpack.security.authc.realms.saml.mock-idp.attributes.mail=http://saml.elastic-cloud.com/attributes/name", + ] + `); + }); + + test('should not add SAML realm args when security is disabled', () => { + const esArgs = resolveEsArgs([['xpack.security.enabled', 'false']], { + ssl: true, + kibanaUrl: 'https://localhost:5601/', + }); + + expect(esArgs).toHaveLength(8); + expect(esArgs).toMatchInlineSnapshot(` + Array [ + "--env", + "xpack.security.enabled=false", + "--env", + "xpack.security.http.ssl.enabled=true", + "--env", + "xpack.security.http.ssl.keystore.path=/usr/share/elasticsearch/config/certs/elasticsearch.p12", + "--env", + "xpack.security.http.ssl.verification_mode=certificate", + ] + `); + }); }); describe('setupServerlessVolumes()', () => { @@ -463,21 +529,29 @@ describe('setupServerlessVolumes()', () => { expect(existsSync(`${serverlessObjectStorePath}/cluster_state/lease`)).toBe(false); }); - test('should add SSL volumes when ssl is passed', async () => { + test('should add SSL and IDP metadata volumes when ssl and kibanaUrl are passed', async () => { mockFs(existingObjectStore); + createMockIdpMetadataMock.mockResolvedValue(''); - const volumeCmd = await setupServerlessVolumes(log, { basePath: baseEsPath, ssl: true }); + const volumeCmd = await setupServerlessVolumes(log, { + basePath: baseEsPath, + ssl: true, + kibanaUrl: 'https://localhost:5603/', + }); + + expect(createMockIdpMetadataMock).toHaveBeenCalledTimes(1); + expect(createMockIdpMetadataMock).toHaveBeenCalledWith('https://localhost:5603/'); const requiredPaths = [ `${baseEsPath}:/objectstore:z`, + SERVERLESS_IDP_METADATA_PATH, ES_P12_PATH, ...SERVERLESS_RESOURCES_PATHS, ]; const pathsNotIncludedInCmd = requiredPaths.filter( (path) => !volumeCmd.some((cmd) => cmd.includes(path)) ); - - expect(volumeCmd).toHaveLength(20); + expect(volumeCmd).toHaveLength(22); expect(pathsNotIncludedInCmd).toEqual([]); }); @@ -543,6 +617,7 @@ describe('runServerlessEsNode()', () => { describe('runServerlessCluster()', () => { test('should start 3 serverless nodes', async () => { + waitUntilClusterReadyMock.mockResolvedValue(); mockFs({ [baseEsPath]: {}, }); @@ -567,7 +642,27 @@ describe('runServerlessCluster()', () => { expect(waitUntilClusterReadyMock.mock.calls[0][0].readyTimeout).toEqual(undefined); }); + test(`should create SAML role mapping when ssl and kibanaUrl are passed`, async () => { + waitUntilClusterReadyMock.mockResolvedValue(); + mockFs({ + [CA_CERT_PATH]: '', + [baseEsPath]: {}, + }); + execa.mockImplementation(() => Promise.resolve({ stdout: '' })); + createMockIdpMetadataMock.mockResolvedValue(''); + + await runServerlessCluster(log, { + basePath: baseEsPath, + waitForReady: true, + ssl: true, + kibanaUrl: 'https://localhost:5601/', + }); + + expect(ensureSAMLRoleMappingMock).toHaveBeenCalledTimes(1); + }); + test(`should wait for the security index`, async () => { + waitUntilClusterReadyMock.mockResolvedValue(); waitForSecurityIndexMock.mockResolvedValue(); mockFs({ [baseEsPath]: {}, @@ -580,6 +675,7 @@ describe('runServerlessCluster()', () => { }); test(`should not wait for the security index when security is disabled`, async () => { + waitUntilClusterReadyMock.mockResolvedValue(); mockFs({ [baseEsPath]: {}, }); diff --git a/packages/kbn-es/src/utils/docker.ts b/packages/kbn-es/src/utils/docker.ts index 1c89339e1a567..73e5e1fc77288 100644 --- a/packages/kbn-es/src/utils/docker.ts +++ b/packages/kbn-es/src/utils/docker.ts @@ -14,12 +14,17 @@ import { Client, ClientOptions, HttpConnection } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import { kibanaPackageJson as pkg, REPO_ROOT } from '@kbn/repo-info'; +import { CA_CERT_PATH, ES_P12_PASSWORD, ES_P12_PATH } from '@kbn/dev-utils'; import { - CA_CERT_PATH, - ES_P12_PASSWORD, - ES_P12_PATH, - kibanaDevServiceAccount, -} from '@kbn/dev-utils'; + MOCK_IDP_REALM_NAME, + MOCK_IDP_ENTITY_ID, + MOCK_IDP_ATTRIBUTE_PRINCIPAL, + MOCK_IDP_ATTRIBUTE_ROLES, + MOCK_IDP_ATTRIBUTE_EMAIL, + MOCK_IDP_ATTRIBUTE_NAME, + ensureSAMLRoleMapping, + createMockIdpMetadata, +} from '@kbn/mock-idp-plugin/common'; import { waitForSecurityIndex } from './wait_for_security_index'; import { createCliError } from '../errors'; @@ -28,6 +33,7 @@ import { SERVERLESS_RESOURCES_PATHS, SERVERLESS_SECRETS_PATH, SERVERLESS_JWKS_PATH, + SERVERLESS_IDP_METADATA_PATH, SERVERLESS_CONFIG_PATH, SERVERLESS_FILES_PATH, SERVERLESS_SECRETS_SSL_PATH, @@ -69,6 +75,8 @@ export interface ServerlessOptions extends EsClusterExecOptions, BaseOptions { background?: boolean; /** Wait for the ES cluster to be ready to serve requests */ waitForReady?: boolean; + /** Fully qualified URL where Kibana is hosted (including base path) */ + kibanaUrl?: string; /** * Resource file(s) to overwrite * (see list of files that can be overwritten under `packages/kbn-es/src/serverless_resources/users`) @@ -460,6 +468,54 @@ export function resolveEsArgs( esArgs.set('ELASTIC_PASSWORD', password); } + // Configure mock identify provider (ES only supports SAML when running in SSL mode) + if ( + ssl && + 'kibanaUrl' in options && + options.kibanaUrl && + esArgs.get('xpack.security.enabled') !== 'false' + ) { + const trimTrailingSlash = (url: string) => (url.endsWith('/') ? url.slice(0, -1) : url); + + esArgs.set(`xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.order`, '0'); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.idp.metadata.path`, + `${SERVERLESS_CONFIG_PATH}secrets/idp_metadata.xml` + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.idp.entity_id`, + MOCK_IDP_ENTITY_ID + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.entity_id`, + trimTrailingSlash(options.kibanaUrl) + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.acs`, + `${trimTrailingSlash(options.kibanaUrl)}/api/security/saml/callback` + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.logout`, + `${trimTrailingSlash(options.kibanaUrl)}/logout` + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.principal`, + MOCK_IDP_ATTRIBUTE_PRINCIPAL + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.groups`, + MOCK_IDP_ATTRIBUTE_ROLES + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.name`, + MOCK_IDP_ATTRIBUTE_EMAIL + ); + esArgs.set( + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.mail`, + MOCK_IDP_ATTRIBUTE_NAME + ); + } + return Array.from(esArgs).flatMap((e) => ['--env', e.join('=')]); } @@ -480,7 +536,7 @@ export function getDockerFileMountPath(hostPath: string) { * Setup local volumes for Serverless ES */ export async function setupServerlessVolumes(log: ToolingLog, options: ServerlessOptions) { - const { basePath, clean, ssl, files, resources } = options; + const { basePath, clean, ssl, kibanaUrl, files, resources } = options; const objectStorePath = resolve(basePath, 'stateless'); log.info(chalk.bold(`Checking for local serverless ES object store at ${objectStorePath}`)); @@ -551,6 +607,16 @@ export async function setupServerlessVolumes(log: ToolingLog, options: Serverles ); } + // Create and add meta data for mock identity provider + if (ssl && kibanaUrl) { + const metadata = await createMockIdpMetadata(kibanaUrl); + await Fsp.writeFile(SERVERLESS_IDP_METADATA_PATH, metadata); + volumeCmds.push( + '--volume', + `${SERVERLESS_IDP_METADATA_PATH}:${SERVERLESS_CONFIG_PATH}secrets/idp_metadata.xml:z` + ); + } + volumeCmds.push( ...getESp12Volume(), ...serverlessResources, @@ -559,7 +625,6 @@ export async function setupServerlessVolumes(log: ToolingLog, options: Serverles `${ ssl ? SERVERLESS_SECRETS_SSL_PATH : SERVERLESS_SECRETS_PATH }:${SERVERLESS_CONFIG_PATH}secrets/secrets.json:z`, - '--volume', `${SERVERLESS_JWKS_PATH}:${SERVERLESS_CONFIG_PATH}secrets/jwks.json:z` ); @@ -661,33 +726,52 @@ export async function runServerlessCluster(log: ToolingLog, options: ServerlessO process.on('SIGINT', () => teardownServerlessClusterSync(log, options)); } + const esNodeUrl = `${options.ssl ? 'https' : 'http'}://${portCmd[1].substring( + 0, + portCmd[1].lastIndexOf(':') + )}`; + + const client = getESClient({ + node: esNodeUrl, + auth: { + username: ELASTIC_SERVERLESS_SUPERUSER, + password: ELASTIC_SERVERLESS_SUPERUSER_PASSWORD, + }, + ...(options.ssl + ? { + tls: { + ca: [fs.readFileSync(CA_CERT_PATH)], + // NOTE: Even though we've added ca into the tls options, we are using 127.0.0.1 instead of localhost + // for the ip which is not validated. As such we are getting the error + // Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the cert's list: + // To work around that we are overriding the function checkServerIdentity too + checkServerIdentity: () => { + return undefined; + }, + }, + } + : {}), + }); + + const readyPromise = waitUntilClusterReady({ client, expectedStatus: 'green', log }).then( + async () => { + if (!options.ssl || !options.kibanaUrl) { + return; + } + + await ensureSAMLRoleMapping(client); + + log.success( + `Created role mapping for mock identity provider. You can now login using ${chalk.bold.cyan( + MOCK_IDP_REALM_NAME + )} realm` + ); + } + ); + if (options.waitForReady) { log.info('Waiting until ES is ready to serve requests...'); - - const esNodeUrl = `${options.ssl ? 'https' : 'http'}://${portCmd[1].substring( - 0, - portCmd[1].lastIndexOf(':') - )}`; - - const client = getESClient({ - node: esNodeUrl, - auth: { bearer: kibanaDevServiceAccount.token }, - ...(options.ssl - ? { - tls: { - ca: [fs.readFileSync(CA_CERT_PATH)], - // NOTE: Even though we've added ca into the tls options, we are using 127.0.0.1 instead of localhost - // for the ip which is not validated. As such we are getting the error - // Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the cert's list: - // To work around that we are overriding the function checkServerIdentity too - checkServerIdentity: () => { - return undefined; - }, - }, - } - : {}), - }); - await waitUntilClusterReady({ client, expectedStatus: 'green', log }); + await readyPromise; if (!options.esArgs || !options.esArgs.includes('xpack.security.enabled=false')) { // If security is not disabled, make sure the security index exists before running the test to avoid flakiness await waitForSecurityIndex({ client, log }); diff --git a/packages/kbn-es/tsconfig.json b/packages/kbn-es/tsconfig.json index 75059c2ef69cd..b40ca33825562 100644 --- a/packages/kbn-es/tsconfig.json +++ b/packages/kbn-es/tsconfig.json @@ -3,13 +3,20 @@ "compilerOptions": { "outDir": "target/types" }, - "include": ["**/*.ts", "**/*.js", "**/*.json"], - "exclude": ["target/**/*"], + "include": [ + "**/*.ts", + "**/*.js", + "**/*.json" + ], + "exclude": [ + "target/**/*" + ], "kbn_references": [ "@kbn/tooling-log", "@kbn/dev-utils", "@kbn/dev-proc-runner", "@kbn/ci-stats-reporter", + "@kbn/mock-idp-plugin", "@kbn/jest-serializers", "@kbn/repo-info" ] diff --git a/packages/kbn-eslint-plugin-i18n/README.mdx b/packages/kbn-eslint-plugin-i18n/README.mdx index 174457477e81a..6b836eb8bb528 100644 --- a/packages/kbn-eslint-plugin-i18n/README.mdx +++ b/packages/kbn-eslint-plugin-i18n/README.mdx @@ -6,22 +6,88 @@ description: Custom ESLint rules to support translations in the Kibana repositor tags: ['kibana', 'dev', 'contributor', 'operations', 'eslint', 'i18n'] --- -`@kbn/eslint-plugin-i18n` is an ESLint plugin providing custom rules for validating JSXCode in the Kibana repo to make sure they are translated. +# Summary -Note: At the moment these rules only work for apps that are inside `/x-pack/plugins`. -If you want to enable this rule on code that is outside of this path, adjust `/helpers/get_i18n_identifier_from_file_path.ts`. +`@kbn/eslint-plugin-i18n` is an ESLint plugin providing custom ESLint rules to help validating code in the Kibana repo in the area of translations. + +The aim of this package is to help engineers type less and have a nicer experience. + +If a rule does not behave as you expect or you have an idea of how these rules can be improved, please reach out to the Observability Knowledge Team or the Kibana Operations team. + +# Rules ## `@kbn/i18n/strings_should_be_translated_with_i18n` -This rule warns engineers to translate their strings by using i18n.translate from the '@kbn/i18n' package. It provides an autofix that takes into account the context of the translatable string in the JSX tree to generate a translation ID. -It kicks in on JSXText elements and specific JSXAttributes (`label` and `aria-label`) which expect a translated value. +This rule warns engineers to translate their strings by using `i18n.translate` from the `@kbn/i18n` package. + +It provides an autofix that takes into account the context of the translatable string in the JSX tree to generate a translation ID. + +This rule kicks in on: + +- JSXText elements; +- specific JSXAttributes (`label` and `aria-label`) which expect a translated value. + +### Example + +This code: + +``` +// Filename: /x-pack/plugins/observability/public/my_component.tsx + +import React from 'react'; +import { EuiText } from '@elastic/eui'; + +function MyComponent() { + return ( + You know, for search + ) +} +``` + +will be autofixed with: + +``` +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText } from '@elastic/eui'; + +function MyComponent() { + return ( + + {i18n.translate('xpack.observability.myComponent.textLabel', { defaultMessage: 'You know, for search' } )} + + ) +} +``` + +If `i18n` has not been imported yet, the autofix will automatically add the import statement as well. + +### Exemptions and exceptions + +A JSXText element or JSXAttribute `label` or `aria-label` of which the value is: + +- wrapped in a `EuiCode` or `EuiBetaBadge` component, +- made up of non alpha characters such as `!@#$%^&*(){}` or numbers, +- wrapped in three backticks, + +are exempt from this rule. + +If this rule kicks in on a string value that you don't like, you can escape it by wrapping the string inside a JSXExpression: `{'my escaped value'}`. + +--- ## `@kbn/i18n/strings_should_be_translated_with_formatted_message` -This rule warns engineers to translate their strings by using `` from the '@kbn/i18n-react' package. It provides an autofix that takes into account the context of the translatable string in the JSX tree and to generate a translation ID. -It kicks in on JSXText elements and specific JSXAttributes (`label` and `aria-label`) which expect a translated value. +This rule warns engineers to translate their strings by using `` from the `@kbn/i18n-react` package. + +It provides an autofix that takes into account the context of the translatable string in the JSX tree and to generate a translation ID. + +This rule kicks in on: -## Exemptions and exceptions +- JSXText elements; +- specific JSXAttributes (`label` and `aria-label`) which expect a translated value. + +### Exemptions and exceptions A JSXText element or JSXAttribute `label` or `aria-label` of which the value is: @@ -32,3 +98,52 @@ A JSXText element or JSXAttribute `label` or `aria-label` of which the value is: are exempt from this rule. If this rule kicks in on a string value that you don't like, you can escape it by wrapping the string inside a JSXExpression: `{'my escaped value'}`. + +--- + +## `@kbn/i18n/i18n_translate_should_start_with_the_right_id` + +This rule checks every instance of `i18n.translate()` if the first parameter passed: + +1. has a string value, +2. if the parameter starts with the correct i18n app identifier for the file. + +It checks the repo for the `i18nrc.json` and `/x-pack/i18nrc.json` files and determines what the right i18n identifier should be. + +If the parameter is missing or does not start with the right i18n identifier, it can autofix the parameter. + +This rule is useful when defining translated values in plain functions (non-JSX), but it works in JSX as well. + +### Example + +This code: + +``` +// Filename: /x-pack/plugins/observability/public/my_function.ts + +function myFunction() { + const translations = [ + { + id: 'copy'; + label: i18n.translate() + } + ] +} +``` + +will be autofixed with: + +``` +import { i18n } from '@kbn/i18n'; + +function myFunction() { + const translations = [ + { + id: 'copy'; + label: i18n.translate('xpack.observability.myFunction.', { defaultMessage: '' }) + } + ] +} +``` + +If `i18n` has not been imported yet, the autofix will automatically add the import statement as well. diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts index 6e01b89b23565..cea9fa1c333d9 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.test.ts @@ -11,17 +11,15 @@ import { getI18nIdentifierFromFilePath } from './get_i18n_identifier_from_file_p const SYSTEMPATH = 'systemPath'; const testMap = [ - ['x-pack/plugins/observability/foo/bar/baz/header_actions.tsx', 'xpack.observability'], - ['x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx', 'xpack.apm'], - ['x-pack/plugins/cases/public/components/foo.tsx', 'xpack.cases'], + ['x-pack/plugins/observability/public/header_actions.tsx', 'xpack.observability'], + ['x-pack/plugins/apm/common/components/app/correlations/correlations_table.tsx', 'xpack.apm'], + ['x-pack/plugins/cases/server/components/foo.tsx', 'xpack.cases'], [ 'x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/toggle_alert_flyout_button.tsx', 'xpack.synthetics', ], - [ - 'packages/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx', - 'app_not_found_in_i18nrc', - ], + ['src/plugins/vis_types/gauge/public/editor/collections.ts', 'visTypeGauge'], + ['packages/kbn-alerts-ui-shared/src/alert_lifecycle_status_badge/index.tsx', 'alertsUIShared'], ]; describe('Get i18n Identifier for file', () => { diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts index d23a42f4ebcfb..7b39d119ee845 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_identifier_from_file_path.ts @@ -14,18 +14,38 @@ export function getI18nIdentifierFromFilePath(fileName: string, cwd: string) { const { dir } = parse(fileName); const relativePathToFile = dir.replace(cwd, ''); - const relativePathArray = relativePathToFile.split('/'); + // We need to match the path of the file that is being worked in with the path + // that is noted in the values inside the i18nrc.json object. + // These values differ depending on which i18nrc.json object you look at (there are multiple) + // so we need to account for both notations. + const relativePathArray = relativePathToFile.includes('src') + ? relativePathToFile.split('/').slice(1) + : relativePathToFile.split('/').slice(2); - const path = `${relativePathArray[2]}/${relativePathArray[3]}`; + const pluginNameIndex = relativePathArray.findIndex( + (el) => el === 'public' || el === 'server' || el === 'common' + ); + + const path = relativePathArray.slice(0, pluginNameIndex).join('/'); const xpackRC = resolve(join(__dirname, '../../../'), 'x-pack/.i18nrc.json'); + const rootRC = resolve(join(__dirname, '../../../'), '.i18nrc.json'); + + const xpackI18nrcFile = fs.readFileSync(xpackRC, 'utf8'); + const xpackI18nrc = JSON.parse(xpackI18nrcFile); + + const rootI18nrcFile = fs.readFileSync(rootRC, 'utf8'); + const rootI18nrc = JSON.parse(rootI18nrcFile); + + const allPaths = { ...xpackI18nrc.paths, ...rootI18nrc.paths }; - const i18nrcFile = fs.readFileSync(xpackRC, 'utf8'); - const i18nrc = JSON.parse(i18nrcFile); + if (Object.keys(allPaths).length === 0) return 'could_not_find_i18nrc'; - return i18nrc && i18nrc.paths - ? findKey(i18nrc.paths, (v) => - Array.isArray(v) ? v.find((e) => e === path) : typeof v === 'string' && v === path - ) ?? 'app_not_found_in_i18nrc' - : 'could_not_find_i18nrc'; + return ( + findKey(allPaths, (value) => + Array.isArray(value) + ? value.find((el) => el === path) + : typeof value === 'string' && value === path + ) ?? 'app_not_found_in_i18nrc' + ); } diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts index cf3a7330f7584..7b81c0f7a6b8c 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts @@ -10,10 +10,10 @@ import { SourceCode } from 'eslint'; export function getI18nImportFixer({ sourceCode, - mode, + translationFunction, }: { sourceCode: SourceCode; - mode: 'i18n.translate' | 'FormattedMessage'; + translationFunction: 'i18n.translate' | 'FormattedMessage'; }) { let existingI18nImportLineIndex = -1; let i18nImportLineToBeAdded = ''; @@ -27,7 +27,7 @@ export function getI18nImportFixer({ * * */ - if (mode === 'i18n.translate') { + if (translationFunction === 'i18n.translate') { existingI18nImportLineIndex = sourceCode.lines.findIndex((l) => l.includes("from '@kbn/i18n'")); const i18nImportLineInSource = sourceCode.lines[existingI18nImportLineIndex]; @@ -46,7 +46,7 @@ export function getI18nImportFixer({ } } - if (mode === 'FormattedMessage') { + if (translationFunction === 'FormattedMessage') { existingI18nImportLineIndex = sourceCode.lines.findIndex((l) => l.includes("from '@kbn/i18n-react'") ); @@ -83,21 +83,27 @@ export function getI18nImportFixer({ return { i18nImportLine: i18nImportLineToBeAdded, rangeToAddI18nImportLine: [start, end] as [number, number], - mode: 'replace', + replaceMode: 'replace', }; } // If the file doesn't have an import line for the translation package yet, we need to add it. // Pretty safe bet to add it underneath the import line for React. - const lineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'")); + let lineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'") || l.includes('*/')); + + if (lineIndex === -1) { + lineIndex = 0; + } + const targetLine = sourceCode.lines[lineIndex]; + // `getIndexFromLoc` is 0-based, so we need to add 1 to the line index. const start = sourceCode.getIndexFromLoc({ line: lineIndex + 1, column: 0 }); const end = start + targetLine.length; return { i18nImportLine: i18nImportLineToBeAdded, rangeToAddI18nImportLine: [start, end] as [number, number], - mode: 'insert', + replaceMode: 'insert', }; } diff --git a/packages/kbn-eslint-plugin-i18n/index.ts b/packages/kbn-eslint-plugin-i18n/index.ts index be5661cf46dec..dd99785204c33 100644 --- a/packages/kbn-eslint-plugin-i18n/index.ts +++ b/packages/kbn-eslint-plugin-i18n/index.ts @@ -8,6 +8,7 @@ import { StringsShouldBeTranslatedWithI18n } from './rules/strings_should_be_translated_with_i18n'; import { StringsShouldBeTranslatedWithFormattedMessage } from './rules/strings_should_be_translated_with_formatted_message'; +import { I18nTranslateShouldStartWithTheRightId } from './rules/i18n_translate_should_start_with_the_right_id'; /** * Custom ESLint rules, add `'@kbn/eslint-plugin-i18n'` to your eslint config to use them @@ -17,4 +18,5 @@ export const rules = { strings_should_be_translated_with_i18n: StringsShouldBeTranslatedWithI18n, strings_should_be_translated_with_formatted_message: StringsShouldBeTranslatedWithFormattedMessage, + i18n_translate_should_start_with_the_right_id: I18nTranslateShouldStartWithTheRightId, }; diff --git a/packages/kbn-eslint-plugin-i18n/kibana.jsonc b/packages/kbn-eslint-plugin-i18n/kibana.jsonc index 72e051941db68..b234cc835ed3a 100644 --- a/packages/kbn-eslint-plugin-i18n/kibana.jsonc +++ b/packages/kbn-eslint-plugin-i18n/kibana.jsonc @@ -1,6 +1,6 @@ { "type": "shared-common", "id": "@kbn/eslint-plugin-i18n", - "owner": "@elastic/obs-knowledge-team", + "owner": ["@elastic/obs-knowledge-team", "@elastic/kibana-operations"], "devOnly": true } diff --git a/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.test.ts b/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.test.ts new file mode 100644 index 0000000000000..49bdb03a4f476 --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.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 { RuleTester } from 'eslint'; +import { + I18nTranslateShouldStartWithTheRightId, + RULE_WARNING_MESSAGE, +} from './i18n_translate_should_start_with_the_right_id'; + +const tsTester = [ + '@typescript-eslint/parser', + new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, + }), +] as const; + +const babelTester = [ + '@babel/eslint-parser', + new RuleTester({ + parser: require.resolve('@babel/eslint-parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + requireConfigFile: false, + babelOptions: { + presets: ['@kbn/babel-preset/node_preset'], + }, + }, + }), +] as const; + +const invalid: RuleTester.InvalidTestCase[] = [ + { + name: 'When a string literal is passed to i18n.translate, it should start with the correct i18n identifier.', + filename: '/x-pack/plugins/observability/public/test_component.ts', + code: ` +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + const foo = i18n.translate('foo'); +}`, + errors: [ + { + line: 5, + message: RULE_WARNING_MESSAGE, + }, + ], + output: ` +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + const foo = i18n.translate('xpack.observability.testComponent.', { defaultMessage: '' }); +}`, + }, + { + name: 'When no string literal is passed to i18n.translate, it should start with the correct i18n identifier.', + filename: '/x-pack/plugins/observability/public/test_component.ts', + code: ` +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + const foo = i18n.translate(); +}`, + errors: [ + { + line: 5, + message: RULE_WARNING_MESSAGE, + }, + ], + output: ` +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + const foo = i18n.translate('xpack.observability.testComponent.', { defaultMessage: '' }); +}`, + }, + { + name: 'When i18n is not imported yet, the rule should add it.', + filename: '/x-pack/plugins/observability/public/test_component.ts', + code: ` +function TestComponent() { + const foo = i18n.translate(); +}`, + errors: [ + { + line: 3, + message: RULE_WARNING_MESSAGE, + }, + ], + output: ` +import { i18n } from '@kbn/i18n'; +function TestComponent() { + const foo = i18n.translate('xpack.observability.testComponent.', { defaultMessage: '' }); +}`, + }, +]; + +const valid: RuleTester.ValidTestCase[] = [ + { + name: invalid[0].name, + filename: invalid[0].filename, + code: invalid[0].output as string, + }, + { + name: invalid[1].name, + filename: invalid[1].filename, + code: invalid[1].output as string, + }, +]; + +for (const [name, tester] of [tsTester, babelTester]) { + describe(name, () => { + tester.run( + '@kbn/i18n_translate_should_start_with_the_right_id', + I18nTranslateShouldStartWithTheRightId, + { + valid, + invalid, + } + ); + }); +} diff --git a/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.ts b/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.ts new file mode 100644 index 0000000000000..d6510ba588a4e --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/rules/i18n_translate_should_start_with_the_right_id.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 { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { Rule } from 'eslint'; +import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path'; +import { getFunctionName } from '../helpers/get_function_name'; +import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; +import { isTruthy } from '../helpers/utils'; + +export const RULE_WARNING_MESSAGE = + 'First parameter passed to i18n.translate should start with the correct i18n identifier for this file. Correct it or use the autofix suggestion.'; + +export const I18nTranslateShouldStartWithTheRightId: Rule.RuleModule = { + meta: { + type: 'suggestion', + fixable: 'code', + }, + create(context) { + const { cwd, filename, getScope, sourceCode, report } = context; + + return { + CallExpression: (node: TSESTree.CallExpression) => { + const { callee } = node; + + if ( + !callee || + !('object' in callee) || + !('property' in callee) || + !('name' in callee.object) || + !('name' in callee.property) || + callee.object.name !== 'i18n' || + callee.property.name !== 'translate' + ) + return; + + const identifier = + Array.isArray(node.arguments) && + node.arguments.length && + 'value' in node.arguments[0] && + typeof node.arguments[0].value === 'string' && + node.arguments[0].value; + + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); + const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; + const functionName = getFunctionName(functionDeclaration); + + // Check if i18n has already been imported into the file + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } = + getI18nImportFixer({ + sourceCode, + translationFunction: 'i18n.translate', + }); + + if (!identifier || (identifier && !identifier.startsWith(`${i18nAppId}.`))) { + report({ + node: node as any, + message: RULE_WARNING_MESSAGE, + fix(fixer) { + return [ + fixer.replaceTextRange( + node.range, + `i18n.translate('${i18nAppId}.${functionName}.', { defaultMessage: '' })` + ), + !hasI18nImportLine && rangeToAddI18nImportLine + ? replaceMode === 'replace' + ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) + : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + : null, + ].filter(isTruthy); + }, + }); + } + }, + } as Rule.RuleListener; + }, +}; diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts index 009fac255fc63..6faf6732f9015 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts @@ -7,7 +7,10 @@ */ import { RuleTester } from 'eslint'; -import { StringsShouldBeTranslatedWithFormattedMessage } from './strings_should_be_translated_with_formatted_message'; +import { + StringsShouldBeTranslatedWithFormattedMessage, + RULE_WARNING_MESSAGE, +} from './strings_should_be_translated_with_formatted_message'; const tsTester = [ '@typescript-eslint/parser', @@ -41,7 +44,7 @@ const babelTester = [ const invalid: RuleTester.InvalidTestCase[] = [ { name: 'A JSX element with a string literal should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -53,7 +56,7 @@ function TestComponent() { errors: [ { line: 6, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -64,7 +67,7 @@ function TestComponent() { return (
) @@ -72,7 +75,7 @@ function TestComponent() { }, { name: 'A JSX element with a string literal that are inside an Eui component should take the component name of the parent into account', - filename: 'x-pack/plugins/observability/public/another_component.tsx', + filename: '/x-pack/plugins/observability/public/another_component.tsx', code: ` import React from 'react'; @@ -90,7 +93,7 @@ function AnotherComponent() { errors: [ { line: 9, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -104,7 +107,7 @@ function AnotherComponent() { @@ -115,7 +118,7 @@ function AnotherComponent() { }, { name: 'When no import of the translation module is present, the import line should be added', - filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', + filename: '/x-pack/plugins/observability/public/yet_another_component.tsx', code: ` import React from 'react'; @@ -129,7 +132,7 @@ function YetAnotherComponent() { errors: [ { line: 7, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -141,7 +144,7 @@ function YetAnotherComponent() {
@@ -150,7 +153,7 @@ function YetAnotherComponent() { }, { name: 'Import lines without the necessary translation module should be updated to include i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { SomeOtherModule } from '@kbn/i18n-react'; @@ -163,7 +166,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -172,13 +175,13 @@ import { SomeOtherModule, FormattedMessage } from '@kbn/i18n-react'; function TestComponent() { return ( - } /> + } /> ) }`, }, { name: 'JSX elements that have a label or aria-label prop with a string value should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -191,7 +194,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -200,13 +203,13 @@ import { FormattedMessage } from '@kbn/i18n-react'; function TestComponent() { return ( - } /> + } /> ) }`, }, { name: 'JSX elements that have a label or aria-label prop with a JSXExpression value that is a string should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -219,7 +222,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -228,7 +231,7 @@ function TestComponent() { function TestComponent() { return ( - } /> + } /> ) }`, }, @@ -237,7 +240,7 @@ function TestComponent() { const valid: RuleTester.ValidTestCase[] = [ { name: 'A JSXText element inside a EuiCode component should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -249,7 +252,7 @@ function TestComponent() { }, { name: 'A JSXText element that contains anything other than alpha characters should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -261,7 +264,7 @@ function TestComponent() { }, { name: 'A JSXText element that is wrapped in three backticks (markdown) should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts index 77b5918951036..ea96cf313d1b4 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts @@ -14,6 +14,8 @@ import { getFunctionName } from '../helpers/get_function_name'; import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; import { cleanString, isTruthy } from '../helpers/utils'; +export const RULE_WARNING_MESSAGE = + 'Strings should be translated with . Use the autofix suggestion or add your own.'; export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { meta: { type: 'suggestion', @@ -44,17 +46,16 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file - const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } = getI18nImportFixer({ sourceCode, - mode: 'FormattedMessage', + translationFunction: 'FormattedMessage', }); // Show warning to developer and offer autofix suggestion report({ node: node as any, - message: - 'Strings should be translated with . Use the autofix suggestion or add your own.', + message: RULE_WARNING_MESSAGE, fix(fixer) { return [ fixer.replaceText( @@ -65,7 +66,7 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { />` ), !hasI18nImportLine && rangeToAddI18nImportLine - ? mode === 'replace' + ? replaceMode === 'replace' ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, @@ -106,17 +107,16 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file. - const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } = getI18nImportFixer({ sourceCode, - mode: 'FormattedMessage', + translationFunction: 'FormattedMessage', }); // Show warning to developer and offer autofix suggestion report({ node: node as any, - message: - 'Strings should be translated with . Use the autofix suggestion or add your own.', + message: RULE_WARNING_MESSAGE, fix(fixer) { return [ fixer.replaceTextRange( @@ -124,7 +124,7 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { `{}` ), !hasI18nImportLine && rangeToAddI18nImportLine - ? mode === 'replace' + ? replaceMode === 'replace' ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts index f470ed885682f..3142d368b0764 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts @@ -7,7 +7,10 @@ */ import { RuleTester } from 'eslint'; -import { StringsShouldBeTranslatedWithI18n } from './strings_should_be_translated_with_i18n'; +import { + StringsShouldBeTranslatedWithI18n, + RULE_WARNING_MESSAGE, +} from './strings_should_be_translated_with_i18n'; const tsTester = [ '@typescript-eslint/parser', @@ -41,7 +44,7 @@ const babelTester = [ const invalid: RuleTester.InvalidTestCase[] = [ { name: 'A JSX element with a string literal should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -53,7 +56,7 @@ function TestComponent() { errors: [ { line: 6, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -62,13 +65,13 @@ import { i18n } from '@kbn/i18n'; function TestComponent() { return ( -
{i18n.translate('app_not_found_in_i18nrc.testComponent.div.thisIsATestLabel', { defaultMessage: 'This is a test' })}
+
{i18n.translate('xpack.observability.testComponent.div.thisIsATestLabel', { defaultMessage: 'This is a test' })}
) }`, }, { name: 'A JSX element with a string literal that are inside an Eui component should take the component name of the parent into account', - filename: 'x-pack/plugins/observability/public/another_component.tsx', + filename: '/x-pack/plugins/observability/public/another_component.tsx', code: ` import React from 'react'; @@ -86,7 +89,7 @@ function AnotherComponent() { errors: [ { line: 9, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -98,7 +101,7 @@ function AnotherComponent() { - {i18n.translate('app_not_found_in_i18nrc.anotherComponent.thisIsATestButtonLabel', { defaultMessage: 'This is a test' })} + {i18n.translate('xpack.observability.anotherComponent.thisIsATestButtonLabel', { defaultMessage: 'This is a test' })} @@ -107,7 +110,7 @@ function AnotherComponent() { }, { name: 'When no import of the translation module is present, the import line should be added', - filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', + filename: '/x-pack/plugins/observability/public/yet_another_component.tsx', code: ` import React from 'react'; @@ -121,7 +124,7 @@ function YetAnotherComponent() { errors: [ { line: 7, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -131,14 +134,14 @@ import { i18n } from '@kbn/i18n'; function YetAnotherComponent() { return (
- {i18n.translate('app_not_found_in_i18nrc.yetAnotherComponent.selectMeSelectLabel', { defaultMessage: 'Select me' })} + {i18n.translate('xpack.observability.yetAnotherComponent.selectMeSelectLabel', { defaultMessage: 'Select me' })}
) }`, }, { name: 'Import lines without the necessary translation module should be updated to include i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { SomeOtherModule } from '@kbn/i18n'; @@ -151,7 +154,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -160,13 +163,13 @@ import { SomeOtherModule, i18n } from '@kbn/i18n'; function TestComponent() { return ( - + ) }`, }, { name: 'JSX elements that have a label or aria-label prop with a string value should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -179,7 +182,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -188,13 +191,13 @@ import { i18n } from '@kbn/i18n'; function TestComponent() { return ( - + ) }`, }, { name: 'JSX elements that have a label or aria-label prop with a JSXExpression value that is a string should be translated with i18n', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -207,7 +210,7 @@ function TestComponent() { errors: [ { line: 7, - message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + message: RULE_WARNING_MESSAGE, }, ], output: ` @@ -216,7 +219,7 @@ import { i18n } from '@kbn/i18n'; function TestComponent() { return ( - + ) }`, }, @@ -225,7 +228,7 @@ function TestComponent() { const valid: RuleTester.ValidTestCase[] = [ { name: 'A JSXText element inside a EuiCode component should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -237,7 +240,7 @@ function TestComponent() { }, { name: 'A JSXText element that contains anything other than alpha characters should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; @@ -249,7 +252,7 @@ function TestComponent() { }, { name: 'A JSXText element that is wrapped in three backticks (markdown) should not be translated', - filename: 'x-pack/plugins/observability/public/test_component.tsx', + filename: '/x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts index fea04d33d555f..ec1630de115e6 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts @@ -14,6 +14,9 @@ import { getFunctionName } from '../helpers/get_function_name'; import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; import { cleanString, isTruthy } from '../helpers/utils'; +export const RULE_WARNING_MESSAGE = + 'Strings should be translated with i18n. Use the autofix suggestion or add your own.'; + export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { meta: { type: 'suggestion', @@ -44,17 +47,16 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file - const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } = getI18nImportFixer({ sourceCode, - mode: 'i18n.translate', + translationFunction: 'i18n.translate', }); // Show warning to developer and offer autofix suggestion report({ node: node as any, - message: - 'Strings should be translated with i18n. Use the autofix suggestion or add your own.', + message: RULE_WARNING_MESSAGE, fix(fixer) { return [ fixer.replaceText( @@ -62,7 +64,7 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { `${whiteSpaces}{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${value}' })}` ), !hasI18nImportLine && rangeToAddI18nImportLine - ? mode === 'replace' + ? replaceMode === 'replace' ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, @@ -103,17 +105,16 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file. - const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } = getI18nImportFixer({ sourceCode, - mode: 'i18n.translate', + translationFunction: 'i18n.translate', }); // Show warning to developer and offer autofix suggestion report({ node: node as any, - message: - 'Strings should be translated with i18n. Use the autofix suggestion or add your own.', + message: RULE_WARNING_MESSAGE, fix(fixer) { return [ fixer.replaceTextRange( @@ -121,7 +122,7 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { `{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${val}' })}` ), !hasI18nImportLine && rangeToAddI18nImportLine - ? mode === 'replace' + ? replaceMode === 'replace' ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, diff --git a/packages/kbn-expandable-flyout/README.md b/packages/kbn-expandable-flyout/README.md index 3e3b620a06b53..d5428bd7e4fcb 100644 --- a/packages/kbn-expandable-flyout/README.md +++ b/packages/kbn-expandable-flyout/README.md @@ -58,6 +58,12 @@ Then use the [React UI component](https://github.com/elastic/kibana/tree/main/pa ``` _where `myPanels` is a list of all the panels that can be rendered in the flyout_ +## State persistence + +The expandable flyout offers 2 ways of managing its state: +- the default behavior saves the state of the flyout in the url. This allows the flyout to be automatically reopened when users refresh the browser page, or when users share a url +- the second way (done by setting the `storage` prop to `memory`) stores the state of the flyout in memory. This means that the flyout will not be reopened when users refresh the browser page, or when users share a url + ## Terminology diff --git a/packages/kbn-expandable-flyout/index.ts b/packages/kbn-expandable-flyout/index.ts index 5a9094d6dbac9..6de82033af04c 100644 --- a/packages/kbn-expandable-flyout/index.ts +++ b/packages/kbn-expandable-flyout/index.ts @@ -7,13 +7,12 @@ */ export { ExpandableFlyout } from './src'; -export { - ExpandableFlyoutProvider, - useExpandableFlyoutContext, - type ExpandableFlyoutContext, -} from './src/context'; -export type { ExpandableFlyoutApi } from './src/context'; +export { useExpandableFlyoutContext, type ExpandableFlyoutContext } from './src/context'; + +export { ExpandableFlyoutProvider } from './src/provider'; export type { ExpandableFlyoutProps } from './src'; export type { FlyoutPanelProps, PanelPath } from './src/types'; + +export { EXPANDABLE_FLYOUT_URL_KEY } from './src/constants'; diff --git a/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx b/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx index 950d406893082..69bdd7050e64a 100644 --- a/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx +++ b/packages/kbn-expandable-flyout/src/components/preview_section.test.tsx @@ -14,10 +14,10 @@ import { PREVIEW_SECTION_CLOSE_BUTTON_TEST_ID, PREVIEW_SECTION_TEST_ID, } from './test_ids'; -import { ExpandableFlyoutContext } from '../context'; +import { ExpandableFlyoutContext, ExpandableFlyoutContextValue } from '../context'; describe('PreviewSection', () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: {}, left: {}, @@ -27,7 +27,7 @@ describe('PreviewSection', () => { }, ], }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const component =
{'component'}
; const left = 500; diff --git a/packages/kbn-expandable-flyout/src/constants.ts b/packages/kbn-expandable-flyout/src/constants.ts new file mode 100644 index 0000000000000..4ee20ebb8e8f4 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EXPANDABLE_FLYOUT_URL_KEY = 'eventFlyout' as const; diff --git a/packages/kbn-expandable-flyout/src/context.tsx b/packages/kbn-expandable-flyout/src/context.tsx index 9738c2a4c867d..6deb5d2bede30 100644 --- a/packages/kbn-expandable-flyout/src/context.tsx +++ b/packages/kbn-expandable-flyout/src/context.tsx @@ -6,202 +6,19 @@ * Side Public License, v 1. */ -import React, { - createContext, - useCallback, - useContext, - useEffect, - useImperativeHandle, - useMemo, - useReducer, -} from 'react'; -import { ActionType } from './actions'; -import { reducer, State } from './reducer'; -import type { FlyoutPanelProps } from './types'; -import { initialState } from './reducer'; +import { createContext, useContext } from 'react'; +import { type ExpandableFlyoutContextValue } from './types'; -export interface ExpandableFlyoutContext { - /** - * Right, left and preview panels - */ - panels: State; - /** - * Open the flyout with left, right and/or preview panels - */ - openFlyout: (panels: { - left?: FlyoutPanelProps; - right?: FlyoutPanelProps; - preview?: FlyoutPanelProps; - }) => void; - /** - * Replaces the current right panel with a new one - */ - openRightPanel: (panel: FlyoutPanelProps) => void; - /** - * Replaces the current left panel with a new one - */ - openLeftPanel: (panel: FlyoutPanelProps) => void; - /** - * Add a new preview panel to the list of current preview panels - */ - openPreviewPanel: (panel: FlyoutPanelProps) => 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 type { ExpandableFlyoutContextValue }; -export const ExpandableFlyoutContext = createContext( +export const ExpandableFlyoutContext = createContext( undefined ); -export type ExpandableFlyoutApi = Pick & { - getState: () => State; -}; - -export interface ExpandableFlyoutProviderProps { - /** - * React children - */ - children: React.ReactNode; - /** - * Triggered whenever flyout state changes. You can use it to store it's state somewhere for instance. - */ - onChanges?: (state: State) => void; - /** - * Triggered whenever flyout is closed. This is independent from the onChanges above. - */ - onClosePanels?: () => void; -} - -/** - * Wrap your plugin with this context for the ExpandableFlyout React component. - */ -export const ExpandableFlyoutProvider = React.forwardRef< - ExpandableFlyoutApi, - ExpandableFlyoutProviderProps ->(({ children, onChanges = () => {}, onClosePanels = () => {} }, ref) => { - const [state, dispatch] = useReducer(reducer, initialState); - - useEffect(() => { - const closed = !state.right; - if (closed) { - // manual close is singalled via separate callback - return; - } - - onChanges(state); - }, [state, onChanges]); - - const openPanels = useCallback( - ({ - right, - left, - preview, - }: { - right?: FlyoutPanelProps; - left?: FlyoutPanelProps; - preview?: FlyoutPanelProps; - }) => dispatch({ type: ActionType.openFlyout, payload: { left, right, preview } }), - [dispatch] - ); - - const openRightPanel = useCallback( - (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openRightPanel, payload: panel }), - [] - ); - - const openLeftPanel = useCallback( - (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), - [] - ); - - const openPreviewPanel = useCallback( - (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), - [] - ); - - const closeRightPanel = useCallback(() => dispatch({ type: ActionType.closeRightPanel }), []); - - const closeLeftPanel = useCallback(() => dispatch({ type: ActionType.closeLeftPanel }), []); - - const closePreviewPanel = useCallback(() => dispatch({ type: ActionType.closePreviewPanel }), []); - - const previousPreviewPanel = useCallback( - () => dispatch({ type: ActionType.previousPreviewPanel }), - [] - ); - - const closePanels = useCallback(() => { - dispatch({ type: ActionType.closeFlyout }); - onClosePanels(); - }, [onClosePanels]); - - useImperativeHandle( - ref, - () => { - return { - openFlyout: openPanels, - getState: () => state, - }; - }, - [openPanels, state] - ); - - 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 => { +export const useExpandableFlyoutContext = (): ExpandableFlyoutContextValue => { const contextValue = useContext(ExpandableFlyoutContext); if (!contextValue) { diff --git a/packages/kbn-expandable-flyout/src/context/memory_state_provider.tsx b/packages/kbn-expandable-flyout/src/context/memory_state_provider.tsx new file mode 100644 index 0000000000000..f70ae9dbbe8de --- /dev/null +++ b/packages/kbn-expandable-flyout/src/context/memory_state_provider.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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, PropsWithChildren, useCallback, useMemo, useReducer } from 'react'; +import { ActionType } from '../actions'; +import { reducer } from '../reducer'; +import type { ExpandableFlyoutContextValue, FlyoutPanelProps } from '../types'; +import { initialState } from '../reducer'; +import { ExpandableFlyoutContext } from '../context'; + +/** + * In-memory state provider for the expandable flyout, for cases when we don't want changes to be persisted + * in the url. + */ +export const MemoryStateProvider: FC> = ({ children }) => { + const [state, dispatch] = useReducer(reducer, initialState); + + const openPanels = useCallback( + ({ + right, + left, + preview, + }: { + right?: FlyoutPanelProps; + left?: FlyoutPanelProps; + preview?: FlyoutPanelProps; + }) => dispatch({ type: ActionType.openFlyout, payload: { left, right, preview } }), + [dispatch] + ); + + const openRightPanel = useCallback( + (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openRightPanel, payload: panel }), + [] + ); + + const openLeftPanel = useCallback( + (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), + [] + ); + + const openPreviewPanel = useCallback( + (panel: FlyoutPanelProps) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), + [] + ); + + const closeRightPanel = useCallback(() => dispatch({ type: ActionType.closeRightPanel }), []); + + const closeLeftPanel = useCallback(() => dispatch({ type: ActionType.closeLeftPanel }), []); + + const closePreviewPanel = useCallback(() => dispatch({ type: ActionType.closePreviewPanel }), []); + + const previousPreviewPanel = useCallback( + () => dispatch({ type: ActionType.previousPreviewPanel }), + [] + ); + + const closePanels = useCallback(() => { + dispatch({ type: ActionType.closeFlyout }); + }, []); + + const contextValue: ExpandableFlyoutContextValue = 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} + + ); +}; diff --git a/packages/kbn-expandable-flyout/src/context/url_state_provider.tsx b/packages/kbn-expandable-flyout/src/context/url_state_provider.tsx new file mode 100644 index 0000000000000..e6f81e23d2fb1 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/context/url_state_provider.tsx @@ -0,0 +1,117 @@ +/* + * Copyright 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, PropsWithChildren, useCallback, useMemo } from 'react'; +import { FlyoutPanelProps, ExpandableFlyoutContextValue } from '../types'; +import { useRightPanel } from '../hooks/use_right_panel'; +import { useLeftPanel } from '../hooks/use_left_panel'; +import { usePreviewPanel } from '../hooks/use_preview_panel'; +import { ExpandableFlyoutContext } from '../context'; +import { State } from '../reducer'; + +/** + * Private component that manages flyout state with url query params + */ +export const UrlStateProvider: FC> = ({ children }) => { + const { setRightPanelState, rightPanelState } = useRightPanel(); + const { setLeftPanelState, leftPanelState } = useLeftPanel(); + const { previewState, setPreviewState } = usePreviewPanel(); + + const panels: State = useMemo( + () => ({ + left: leftPanelState, + right: rightPanelState, + preview: previewState || [], + }), + [leftPanelState, previewState, rightPanelState] + ); + + const openPanels = useCallback( + ({ + right, + left, + preview, + }: { + right?: FlyoutPanelProps; + left?: FlyoutPanelProps; + preview?: FlyoutPanelProps; + }) => { + setRightPanelState(right); + setLeftPanelState(left); + setPreviewState(preview ? [preview] : []); + }, + [setRightPanelState, setLeftPanelState, setPreviewState] + ); + + const openRightPanel = useCallback( + (panel: FlyoutPanelProps) => { + setRightPanelState(panel); + }, + [setRightPanelState] + ); + + const openLeftPanel = useCallback( + (panel: FlyoutPanelProps) => setLeftPanelState(panel), + [setLeftPanelState] + ); + + const openPreviewPanel = useCallback( + (panel: FlyoutPanelProps) => setPreviewState([...(previewState ?? []), panel]), + [previewState, setPreviewState] + ); + + const closeRightPanel = useCallback(() => setRightPanelState(undefined), [setRightPanelState]); + + const closeLeftPanel = useCallback(() => setLeftPanelState(undefined), [setLeftPanelState]); + + const closePreviewPanel = useCallback(() => setPreviewState([]), [setPreviewState]); + + const previousPreviewPanel = useCallback( + () => setPreviewState(previewState?.slice(0, previewState.length - 1)), + [previewState, setPreviewState] + ); + + const closePanels = useCallback(() => { + setRightPanelState(undefined); + setLeftPanelState(undefined); + setPreviewState([]); + }, [setRightPanelState, setLeftPanelState, setPreviewState]); + + const contextValue: ExpandableFlyoutContextValue = useMemo( + () => ({ + panels, + openFlyout: openPanels, + openRightPanel, + openLeftPanel, + openPreviewPanel, + closeRightPanel, + closeLeftPanel, + closePreviewPanel, + closeFlyout: closePanels, + previousPreviewPanel, + }), + [ + panels, + openPanels, + openRightPanel, + openLeftPanel, + openPreviewPanel, + closeRightPanel, + closeLeftPanel, + closePreviewPanel, + closePanels, + previousPreviewPanel, + ] + ); + + return ( + + {children} + + ); +}; diff --git a/packages/kbn-expandable-flyout/src/hooks/use_left_panel.ts b/packages/kbn-expandable-flyout/src/hooks/use_left_panel.ts new file mode 100644 index 0000000000000..2a3e4212a06fe --- /dev/null +++ b/packages/kbn-expandable-flyout/src/hooks/use_left_panel.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useUrlState } from '@kbn/url-state'; +import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants'; +import { FlyoutPanelProps } from '../types'; + +/** + * This hook stores state in the URL + */ +export const useLeftPanel = () => { + const [leftPanelState, setLeftPanelState] = useUrlState( + EXPANDABLE_FLYOUT_URL_KEY, + 'leftPanel' + ); + + return { leftPanelState, setLeftPanelState } as const; +}; diff --git a/packages/kbn-expandable-flyout/src/hooks/use_preview_panel.ts b/packages/kbn-expandable-flyout/src/hooks/use_preview_panel.ts new file mode 100644 index 0000000000000..5e9cfddb93ba4 --- /dev/null +++ b/packages/kbn-expandable-flyout/src/hooks/use_preview_panel.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useUrlState } from '@kbn/url-state'; +import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants'; +import { FlyoutPanelProps } from '../types'; + +/** + * This hook stores state in the URL + */ +export const usePreviewPanel = () => { + const [previewState, setPreviewState] = useUrlState( + EXPANDABLE_FLYOUT_URL_KEY, + 'preview' + ); + + return { previewState, setPreviewState } as const; +}; diff --git a/packages/kbn-expandable-flyout/src/hooks/use_right_panel.ts b/packages/kbn-expandable-flyout/src/hooks/use_right_panel.ts new file mode 100644 index 0000000000000..2bce75d65f23e --- /dev/null +++ b/packages/kbn-expandable-flyout/src/hooks/use_right_panel.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useUrlState } from '@kbn/url-state'; +import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants'; +import { FlyoutPanelProps } from '../types'; + +/** + * This hook stores state in the URL + */ +export const useRightPanel = () => { + const [rightPanelState, setRightPanelState] = useUrlState( + EXPANDABLE_FLYOUT_URL_KEY, + 'rightPanel' + ); + + return { rightPanelState, setRightPanelState } as const; +}; diff --git a/packages/kbn-expandable-flyout/src/index.stories.tsx b/packages/kbn-expandable-flyout/src/index.stories.tsx index 2bbc26c3363f3..615621bf9a571 100644 --- a/packages/kbn-expandable-flyout/src/index.stories.tsx +++ b/packages/kbn-expandable-flyout/src/index.stories.tsx @@ -19,7 +19,7 @@ import { EuiTitle, } from '@elastic/eui'; import { ExpandableFlyout } from '.'; -import { ExpandableFlyoutContext } from './context'; +import { ExpandableFlyoutContext, ExpandableFlyoutContextValue } from './context'; export default { component: ExpandableFlyout, @@ -100,7 +100,7 @@ const registeredPanels = [ ]; export const Right: Story = () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: { id: 'right', @@ -109,7 +109,7 @@ export const Right: Story = () => { preview: [], }, closeFlyout: () => window.alert('closeFlyout api'), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; return ( @@ -119,7 +119,7 @@ export const Right: Story = () => { }; export const Left: Story = () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: { id: 'right', @@ -130,7 +130,7 @@ export const Left: Story = () => { preview: [], }, closeFlyout: () => window.alert('closeFlyout api'), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; return ( @@ -140,7 +140,7 @@ export const Left: Story = () => { }; export const Preview: Story = () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: { id: 'right', @@ -156,7 +156,7 @@ export const Preview: Story = () => { }, closePreviewPanel: () => window.alert('closePreviewPanel api'), closeFlyout: () => window.alert('closeFlyout api'), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; return ( @@ -166,7 +166,7 @@ export const Preview: Story = () => { }; export const MultiplePreviews: Story = () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: { id: 'right', @@ -186,7 +186,7 @@ export const MultiplePreviews: Story = () => { closePreviewPanel: () => window.alert('closePreviewPanel api'), previousPreviewPanel: () => window.alert('previousPreviewPanel api'), closeFlyout: () => window.alert('closeFlyout api'), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; return ( diff --git a/packages/kbn-expandable-flyout/src/index.test.tsx b/packages/kbn-expandable-flyout/src/index.test.tsx index c6da99ad01777..46ae2dce508f2 100644 --- a/packages/kbn-expandable-flyout/src/index.test.tsx +++ b/packages/kbn-expandable-flyout/src/index.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { Panel } from './types'; +import { ExpandableFlyoutContextValue, Panel } from './types'; import { ExpandableFlyout } from '.'; import { LEFT_SECTION_TEST_ID, @@ -26,13 +26,13 @@ describe('ExpandableFlyout', () => { ]; it(`shouldn't render flyout if no panels`, () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: undefined, left: undefined, preview: [], }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const result = render( @@ -44,7 +44,7 @@ describe('ExpandableFlyout', () => { }); it('should render right section', () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: { id: 'key', @@ -52,7 +52,7 @@ describe('ExpandableFlyout', () => { left: {}, preview: [], }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId } = render( @@ -64,7 +64,7 @@ describe('ExpandableFlyout', () => { }); it('should render left section', () => { - const context: ExpandableFlyoutContext = { + const context = { panels: { right: {}, left: { @@ -72,7 +72,7 @@ describe('ExpandableFlyout', () => { }, preview: [], }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId } = render( @@ -84,7 +84,7 @@ describe('ExpandableFlyout', () => { }); it('should render preview section', () => { - const context: ExpandableFlyoutContext = { + const context: ExpandableFlyoutContextValue = { panels: { right: {}, left: {}, @@ -94,7 +94,7 @@ describe('ExpandableFlyout', () => { }, ], }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId } = render( diff --git a/packages/kbn-expandable-flyout/src/index.tsx b/packages/kbn-expandable-flyout/src/index.tsx index 17613be6859b7..e7f6c3fcafb23 100644 --- a/packages/kbn-expandable-flyout/src/index.tsx +++ b/packages/kbn-expandable-flyout/src/index.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { EuiFlyoutProps } from '@elastic/eui'; import { EuiFlexGroup, EuiFlyout } from '@elastic/eui'; import { useSectionSizes } from './hooks/use_sections_sizes'; @@ -25,10 +25,6 @@ export interface ExpandableFlyoutProps extends Omit { * List of all registered panels available for render */ registeredPanels: Panel[]; - /** - * Propagate out EuiFlyout onClose event - */ - handleOnFlyoutClosed?: () => void; } /** @@ -40,18 +36,13 @@ export interface ExpandableFlyoutProps extends Omit { */ export const ExpandableFlyout: React.FC = ({ registeredPanels, - handleOnFlyoutClosed, ...flyoutProps }) => { const windowWidth = useWindowSize(); - const { panels, closeFlyout } = useExpandableFlyoutContext(); - const { left, right, preview } = panels; + const { closeFlyout, panels } = useExpandableFlyoutContext(); - const onClose = useCallback(() => { - if (handleOnFlyoutClosed) handleOnFlyoutClosed(); - closeFlyout(); - }, [closeFlyout, handleOnFlyoutClosed]); + const { left, right, preview } = panels; const leftSection = useMemo( () => registeredPanels.find((panel) => panel.key === left?.id), @@ -69,7 +60,7 @@ export const ExpandableFlyout: React.FC = ({ ? mostRecentPreview?.params?.banner : undefined; - const showBackButton = preview && preview.length > 1; + const showBackButton = !!preview && preview.length > 1; const previewSection = useMemo( () => registeredPanels.find((panel) => panel.key === mostRecentPreview?.id), [mostRecentPreview, registeredPanels] @@ -86,13 +77,13 @@ export const ExpandableFlyout: React.FC = ({ showPreview, }); - const hideFlyout = !left && !right && !preview.length; + const hideFlyout = !left && !right && !preview?.length; if (hideFlyout) { return null; } return ( - + > = ({ + children, + storage = 'url', +}) => { + if (storage === 'memory') { + return {children}; + } + + return {children}; +}; diff --git a/packages/kbn-expandable-flyout/src/types.ts b/packages/kbn-expandable-flyout/src/types.ts index bfe64c4599085..92c4af79024b8 100644 --- a/packages/kbn-expandable-flyout/src/types.ts +++ b/packages/kbn-expandable-flyout/src/types.ts @@ -7,6 +7,55 @@ */ import React from 'react'; +import { State } from './reducer'; + +export interface ExpandableFlyoutContextValue { + /** + * Right, left and preview panels + */ + panels: State; + + /** + * Open the flyout with left, right and/or preview panels + */ + openFlyout: (panels: { + left?: FlyoutPanelProps; + right?: FlyoutPanelProps; + preview?: FlyoutPanelProps; + }) => void; + /** + * Replaces the current right panel with a new one + */ + openRightPanel: (panel: FlyoutPanelProps) => void; + /** + * Replaces the current left panel with a new one + */ + openLeftPanel: (panel: FlyoutPanelProps) => void; + /** + * Add a new preview panel to the list of current preview panels + */ + openPreviewPanel: (panel: FlyoutPanelProps) => 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 interface PanelPath { /** diff --git a/packages/kbn-expandable-flyout/tsconfig.json b/packages/kbn-expandable-flyout/tsconfig.json index d1755389bcddc..9a5dcbaf03048 100644 --- a/packages/kbn-expandable-flyout/tsconfig.json +++ b/packages/kbn-expandable-flyout/tsconfig.json @@ -19,6 +19,7 @@ "target/**/*" ], "kbn_references": [ - "@kbn/i18n" + "@kbn/i18n", + "@kbn/url-state" ] } diff --git a/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap b/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap index da0f6a4560640..e38a7427e98d6 100644 --- a/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap +++ b/packages/kbn-generate-csv/src/__snapshots__/generate_csv.test.ts.snap @@ -66,6 +66,77 @@ exports[`CsvGenerator formulas escapes formula values in a header, doesn't warn `; exports[`CsvGenerator keeps order of the columns during the scroll 1`] = ` +Array [ + Array [ + "Requesting PIT for: [logstash-*]...", + ], + Array [ + "Opened PIT ID: oju9fs3698s3[39 bytes]", + ], + Array [ + "Executing search request with PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received total hits: 3. Accuracy: unknown.", + ], + Array [ + "Result details: {\\"rawResponse\\":{\\"took\\":1,\\"timed_out\\":false,\\"_shards\\":{\\"total\\":1,\\"successful\\":1,\\"failed\\":0,\\"skipped\\":0},\\"hits\\":{\\"total\\":3,\\"max_score\\":0},\\"pit_id\\":\\"oju9fs3698s3[39 bytes]\\"}}", + ], + Array [ + "Received PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received search_after: [undefined]", + ], + Array [ + "Building CSV header row", + ], + Array [ + "Building 1 CSV data rows", + ], + Array [ + "Executing search request with PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received total hits: 3. Accuracy: unknown.", + ], + Array [ + "Result details: {\\"rawResponse\\":{\\"took\\":1,\\"timed_out\\":false,\\"_shards\\":{\\"total\\":1,\\"successful\\":1,\\"failed\\":0,\\"skipped\\":0},\\"hits\\":{\\"total\\":3,\\"max_score\\":0},\\"pit_id\\":\\"oju9fs3698s3[39 bytes]\\"}}", + ], + Array [ + "Received PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received search_after: [undefined]", + ], + Array [ + "Building 1 CSV data rows", + ], + Array [ + "Executing search request with PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received total hits: 3. Accuracy: unknown.", + ], + Array [ + "Result details: {\\"rawResponse\\":{\\"took\\":1,\\"timed_out\\":false,\\"_shards\\":{\\"total\\":1,\\"successful\\":1,\\"failed\\":0,\\"skipped\\":0},\\"hits\\":{\\"total\\":3,\\"max_score\\":0},\\"pit_id\\":\\"oju9fs3698s3[39 bytes]\\"}}", + ], + Array [ + "Received PIT ID: [oju9fs3698s3[39 bytes]]", + ], + Array [ + "Received search_after: [undefined]", + ], + Array [ + "Building 1 CSV data rows", + ], + Array [ + "Closing PIT oju9fs3698s3[39 bytes]", + ], +] +`; + +exports[`CsvGenerator keeps order of the columns during the scroll 2`] = ` "\\"_id\\",\\"_index\\",\\"_score\\",a,b \\"'-\\",\\"'-\\",\\"'-\\",a1,b1 \\"'-\\",\\"'-\\",\\"'-\\",\\"'-\\",b2 diff --git a/packages/kbn-generate-csv/src/generate_csv.test.ts b/packages/kbn-generate-csv/src/generate_csv.test.ts index 22857f37afdaf..22269054a61d9 100644 --- a/packages/kbn-generate-csv/src/generate_csv.test.ts +++ b/packages/kbn-generate-csv/src/generate_csv.test.ts @@ -393,6 +393,8 @@ describe('CsvGenerator', () => { }) ); + const debugLogSpy = jest.spyOn(mockLogger, 'debug'); + const generateCsv = new CsvGenerator( createMockJob({ searchSource: {}, columns: [] }), mockConfig, @@ -411,6 +413,8 @@ describe('CsvGenerator', () => { ); await generateCsv.generateData(); + expect(debugLogSpy.mock.calls).toMatchSnapshot(); + expect(content).toMatchSnapshot(); }); @@ -896,6 +900,82 @@ describe('CsvGenerator', () => { `); }); + describe('debug logging', () => { + it('logs the the total hits relation if relation is provided', async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + took: 1, + timed_out: false, + pit_id: mockPitId, + _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, + hits: { hits: [], total: { relation: 'eq', value: 12345 }, max_score: 0 }, + }, + }) + ); + + const debugLogSpy = jest.spyOn(mockLogger, 'debug'); + + const generateCsv = new CsvGenerator( + createMockJob({ columns: ['date', 'ip', 'message'] }), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + mockLogger, + stream + ); + + await generateCsv.generateData(); + + expect(debugLogSpy).toHaveBeenCalledWith('Received total hits: 12345. Accuracy: eq.'); + }); + + it('logs the the total hits relation as "unknown" if relation is not provided', async () => { + mockDataClient.search = jest.fn().mockImplementation(() => + Rx.of({ + rawResponse: { + took: 1, + timed_out: false, + pit_id: mockPitId, + _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, + hits: { hits: [], total: 12345, max_score: 0 }, + }, + }) + ); + + const debugLogSpy = jest.spyOn(mockLogger, 'debug'); + + const generateCsv = new CsvGenerator( + createMockJob({ columns: ['date', 'ip', 'message'] }), + mockConfig, + { + es: mockEsClient, + data: mockDataClient, + uiSettings: uiSettingsClient, + }, + { + searchSourceStart: mockSearchSourceService, + fieldFormatsRegistry: mockFieldFormatsRegistry, + }, + new CancellationToken(), + mockLogger, + stream + ); + + await generateCsv.generateData(); + + expect(debugLogSpy).toHaveBeenCalledWith('Received total hits: 12345. Accuracy: unknown.'); + }); + }); + it('will return partial data if the scroll or search fails', async () => { mockDataClient.search = jest.fn().mockImplementation(() => { throw new esErrors.ResponseError({ diff --git a/packages/kbn-generate-csv/src/generate_csv.ts b/packages/kbn-generate-csv/src/generate_csv.ts index 59e69e9989c8e..22e2916bf6fd7 100644 --- a/packages/kbn-generate-csv/src/generate_csv.ts +++ b/packages/kbn-generate-csv/src/generate_csv.ts @@ -11,7 +11,11 @@ import type { Writable } from 'stream'; import { errors as esErrors, estypes } from '@elastic/elasticsearch'; import type { IScopedClusterClient, IUiSettingsClient, Logger } from '@kbn/core/server'; -import type { ISearchSource, ISearchStartSearchSource } from '@kbn/data-plugin/common'; +import type { + IKibanaSearchResponse, + ISearchSource, + ISearchStartSearchSource, +} from '@kbn/data-plugin/common'; import { ES_SEARCH_STRATEGY, cellHasFormulas, tabifyDocs } from '@kbn/data-plugin/common'; import type { IScopedSearchClient } from '@kbn/data-plugin/server'; import type { Datatable } from '@kbn/expressions-plugin/server'; @@ -94,6 +98,38 @@ export class CsvGenerator { return pitId; } + /** + * @param clientDetails: Details from the data.search client + * @param results: Raw data from ES + */ + private logResults( + clientDetails: Omit, 'rawResponse'>, + results: estypes.SearchResponse + ) { + const { hits: resultsHits, ...headerWithPit } = results; + const { hits, ...hitsMeta } = resultsHits; + const trackedTotal = resultsHits.total as estypes.SearchTotalHits; + const currentTotal = trackedTotal?.value ?? resultsHits.total; + + const totalAccuracy = trackedTotal?.relation ?? 'unknown'; + this.logger.debug(`Received total hits: ${currentTotal}. Accuracy: ${totalAccuracy}.`); + + // reconstruct the data.search response (w/out the data) for logging + const { pit_id: newPitId, ...header } = headerWithPit; + const logInfo = { + ...clientDetails, + rawResponse: { + ...header, + hits: hitsMeta, + pit_id: `${this.formatPit(newPitId)}`, + }, + }; + this.logger.debug(`Result details: ${JSON.stringify(logInfo)}`); + + // use the most recently received id for the next search request + this.logger.debug(`Received PIT ID: [${this.formatPit(results.pit_id)}]`); + } + private async doSearch( searchSource: ISearchSource, settings: CsvExportSettings, @@ -117,25 +153,20 @@ export class CsvGenerator { throw new Error('Could not retrieve the search body!'); } - const searchParams = { - params: { - body: searchBody, - }, - }; - - let results: estypes.SearchResponse | undefined; + const searchParams = { params: { body: searchBody } }; + let results: estypes.SearchResponse; try { - results = ( - await lastValueFrom( - this.clients.data.search(searchParams, { - strategy: ES_SEARCH_STRATEGY, - transport: { - maxRetries: 0, // retrying reporting jobs is handled in the task manager scheduling logic - requestTimeout: scrollSettings.duration, - }, - }) - ) - ).rawResponse; + const { rawResponse, ...rawDetails } = await lastValueFrom( + this.clients.data.search(searchParams, { + strategy: ES_SEARCH_STRATEGY, + transport: { + maxRetries: 0, // retrying reporting jobs is handled in the task manager scheduling logic + requestTimeout: settings.scroll.duration, + }, + }) + ); + results = rawResponse; + this.logResults(rawDetails, rawResponse); } catch (err) { this.logger.error(`CSV export search error: ${err}`); throw err; @@ -327,7 +358,6 @@ export class CsvGenerator { let first = true; let currentRecord = -1; let totalRecords: number | undefined; - let totalRelation = 'eq'; let searchAfter: estypes.SortResults | undefined; let pitId = await this.openPointInTime(indexPatternTitle, settings); @@ -360,47 +390,31 @@ export class CsvGenerator { searchSource.setField('pit', { id: pitId, keep_alive: settings.scroll.duration }); const results = await this.doSearch(searchSource, settings, searchAfter); - - const { hits } = results; - if (first && hits.total != null) { - if (typeof hits.total === 'number') { - totalRecords = hits.total; - } else { - totalRecords = hits.total?.value; - totalRelation = hits.total?.relation ?? 'unknown'; - } - this.logger.info(`Total hits ${totalRelation} ${totalRecords}.`); - } - if (!results) { this.logger.warn(`Search results are undefined!`); break; } - const { - hits: { hits: _hits, ...hitsMeta }, - ...headerWithPit - } = results; + const { hits: resultsHits } = results; + const { hits, total } = resultsHits; + const trackedTotal = total as estypes.SearchTotalHits; + const currentTotal = trackedTotal?.value ?? total; - const { pit_id: newPitId, ...header } = headerWithPit; - - const logInfo = { - header: { pit_id: `${this.formatPit(newPitId)}`, ...header }, - hitsMeta, - }; - this.logger.debug(`Results metadata: ${JSON.stringify(logInfo)}`); + if (first) { + // export stops when totalRecords have been accumulated (or the results have run out) + totalRecords = currentTotal; + } // use the most recently received id for the next search request - this.logger.debug(`Received PIT ID: [${this.formatPit(results.pit_id)}]`); pitId = results.pit_id ?? pitId; // Update last sort results for next query. PIT is used, so the sort results // automatically include _shard_doc as a tiebreaker - searchAfter = hits.hits[hits.hits.length - 1]?.sort as estypes.SortResults | undefined; + searchAfter = hits[hits.length - 1]?.sort as estypes.SortResults | undefined; this.logger.debug(`Received search_after: [${searchAfter}]`); // check for shard failures, log them and add a warning if found - const { _shards: shards } = header; + const { _shards: shards } = results; if (shards.failures) { shards.failures.forEach(({ reason }) => { warnings.push(`Shard failure: ${JSON.stringify(reason)}`); @@ -499,6 +513,9 @@ export class CsvGenerator { }; } + /** + * Method to avoid logging the entire PIT: it could be megabytes long + */ private formatPit(pitId: string | undefined) { const byteSize = pitId ? Buffer.byteLength(pitId, 'utf-8') : 0; return pitId?.substring(0, 12) + `[${byteSize} bytes]`; diff --git a/packages/kbn-language-documentation-popover/src/components/documentation_content.test.tsx b/packages/kbn-language-documentation-popover/src/components/documentation_content.test.tsx index 6d91cc403795e..e0d7e3c28dbea 100644 --- a/packages/kbn-language-documentation-popover/src/components/documentation_content.test.tsx +++ b/packages/kbn-language-documentation-popover/src/components/documentation_content.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; +import { Markdown } from '@kbn/kibana-react-plugin/public'; import { LanguageDocumentationPopoverContent } from './documentation_content'; describe('###Documentation popover content', () => { @@ -24,11 +25,11 @@ describe('###Documentation popover content', () => { items: [ { label: 'Section two item 1', - description: Section 2 item 1 description, + description: , }, { label: 'Section two item 2', - description: Section 2 item 2 description, + description: , }, ], }, @@ -52,7 +53,7 @@ describe('###Documentation popover content', () => { }); }); - test('Documentation component should list all sections that match the search input', () => { + test('Documentation component should list all sections that match the search input when title matches', () => { const component = mountWithIntl( ); @@ -69,4 +70,25 @@ describe('###Documentation popover content', () => { expect(sectionsLabels.length).toBe(1); expect(sectionsLabels.text()).toEqual('Section one'); }); + + test('Documentation component should list all sections that match the search input when description matches', () => { + const component = mountWithIntl( + + ); + const searchBox = component.find('[data-test-subj="language-documentation-navigation-search"]'); + act(() => { + searchBox.at(0).prop('onChange')!({ + target: { value: 'item 2 description' }, + } as React.ChangeEvent); + }); + + component.update(); + + const sectionsLabels = findTestSubject(component, 'language-documentation-navigation-title'); + expect(sectionsLabels.length).toBe(1); + }); }); diff --git a/packages/kbn-language-documentation-popover/src/components/documentation_content.tsx b/packages/kbn-language-documentation-popover/src/components/documentation_content.tsx index b7c2e800bbaf5..0f24e233a4f28 100644 --- a/packages/kbn-language-documentation-popover/src/components/documentation_content.tsx +++ b/packages/kbn-language-documentation-popover/src/components/documentation_content.tsx @@ -20,6 +20,7 @@ import { EuiHighlight, EuiSpacer, } from '@elastic/eui'; +import { elementToString } from '../utils/element_to_string'; import './documentation.scss'; @@ -35,9 +36,11 @@ export interface LanguageDocumentationSections { interface DocumentationProps { language: string; sections?: LanguageDocumentationSections; + // if sets to true, allows searching in the markdown description + searchInDescription?: boolean; } -function DocumentationContent({ language, sections }: DocumentationProps) { +function DocumentationContent({ language, sections, searchInDescription }: DocumentationProps) { const [selectedSection, setSelectedSection] = useState(); const scrollTargets = useRef>({}); @@ -55,7 +58,13 @@ function DocumentationContent({ language, sections }: DocumentationProps) { .map((group) => { const items = group.items.filter((helpItem) => { return ( - !normalizedSearchText || helpItem.label.toLocaleLowerCase().includes(normalizedSearchText) + !normalizedSearchText || + helpItem.label.toLocaleLowerCase().includes(normalizedSearchText) || + // Converting the JSX element to a string first + (searchInDescription && + elementToString(helpItem.description) + ?.toLocaleLowerCase() + .includes(normalizedSearchText)) ); }); return { ...group, items }; diff --git a/packages/kbn-language-documentation-popover/src/components/documentation_popover.tsx b/packages/kbn-language-documentation-popover/src/components/documentation_popover.tsx index 8ff16737337aa..db66d69d7173f 100644 --- a/packages/kbn-language-documentation-popover/src/components/documentation_popover.tsx +++ b/packages/kbn-language-documentation-popover/src/components/documentation_popover.tsx @@ -17,9 +17,15 @@ interface DocumentationPopoverProps { language: string; sections?: LanguageDocumentationSections; buttonProps?: Omit; + searchInDescription?: boolean; } -function DocumentationPopover({ language, sections, buttonProps }: DocumentationPopoverProps) { +function DocumentationPopover({ + language, + sections, + buttonProps, + searchInDescription, +}: DocumentationPopoverProps) { const [isHelpOpen, setIsHelpOpen] = useState(false); const toggleDocumentationPopover = useCallback(() => { @@ -50,7 +56,11 @@ function DocumentationPopover({ language, sections, buttonProps }: Documentation } > - + ); } diff --git a/packages/kbn-language-documentation-popover/src/utils/element_to_string.test.tsx b/packages/kbn-language-documentation-popover/src/utils/element_to_string.test.tsx new file mode 100644 index 0000000000000..42ca61cca472b --- /dev/null +++ b/packages/kbn-language-documentation-popover/src/utils/element_to_string.test.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 React from 'react'; +import { Markdown } from '@kbn/kibana-react-plugin/public'; +import { elementToString } from './element_to_string'; + +describe('elementToString', () => { + test('Should return empty string if no element is given', () => { + const text = elementToString(undefined); + expect(text).toEqual(''); + }); + + test('Should return empty string if no markdown is passed', () => { + const text = elementToString(Meow); + expect(text).toEqual(''); + }); + + test('Should convert to string if markdown is passed', () => { + const text = elementToString(); + expect(text).toEqual('## Markdown goes here '); + }); + + test('Should convert to string if children with markdown are passed', () => { + const text = elementToString( + <> +

Meow

+ + + ); + expect(text).toEqual('## Markdown goes here '); + }); +}); diff --git a/packages/kbn-language-documentation-popover/src/utils/element_to_string.ts b/packages/kbn-language-documentation-popover/src/utils/element_to_string.ts new file mode 100644 index 0000000000000..f13bb652f662a --- /dev/null +++ b/packages/kbn-language-documentation-popover/src/utils/element_to_string.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 React, { isValidElement } from 'react'; + +function nonNullable(v: T): v is NonNullable { + return v != null; +} + +/** + * Gets the JSX.Element as the input. It returns the markdown as string. + * If the children are not markdown it will return an empty string. + */ +export function elementToString(element?: JSX.Element): string { + if (!element) { + return ''; + } + const props = element.props; + if (props && 'markdown' in props) { + return String(props.markdown); + } else if (props && 'children' in props && Array.isArray(props.children)) { + return props.children.reduce((text: string, child: React.ReactNode): string => { + const validChildren = React.Children.toArray(child).filter(nonNullable); + if (isValidElement(child) && validChildren.length > 0) { + return text.concat(elementToString(child)); + } + return text; + }, ''); + } + return ''; +} diff --git a/packages/kbn-language-documentation-popover/tsconfig.json b/packages/kbn-language-documentation-popover/tsconfig.json index 82710b41d10b4..a0c043b8a15e5 100644 --- a/packages/kbn-language-documentation-popover/tsconfig.json +++ b/packages/kbn-language-documentation-popover/tsconfig.json @@ -14,6 +14,7 @@ "kbn_references": [ "@kbn/i18n", "@kbn/test-jest-helpers", + "@kbn/kibana-react-plugin", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/types.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/types.ts index 428f6aacce133..68d632db2f03a 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/types.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/types.ts @@ -18,6 +18,7 @@ import type { XYLayerConfig, FillStyle, } from '@kbn/lens-plugin/public'; + export type LensAttributes = TypedLensByValueInput['attributes']; // Attributes @@ -61,7 +62,6 @@ export interface ChartLayer { getDataView(): DataView | undefined; } -// Chart export interface Chart { getTitle(): string; getVisualizationType(): string; @@ -70,6 +70,8 @@ export interface Chart { getReferences(): SavedObjectReference[]; getDataViews(): DataView[]; } + +// Chart export interface ChartConfig< TLayer extends ChartLayer | Array> > { @@ -91,3 +93,5 @@ export type StaticValueConfig = Omit & { fill?: FillStyle; value: string; }; + +export type VisualizationTypes = 'lnsXY' | 'lnsMetric'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts new file mode 100644 index 0000000000000..f2d6a056c09db --- /dev/null +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const XY_ID = 'lnsXY'; +export const METRIC_ID = 'lnsMetric'; + +export const METRIC_TREND_LINE_ID = 'metricTrendline'; +export const XY_REFERENCE_LINE_ID = 'referenceLine'; +export const XY_DATA_ID = 'data'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/index.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/index.ts index 7927ff37b2f16..ffafd8983fe5c 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/index.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/index.ts @@ -5,8 +5,37 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import type { DataView } from '@kbn/data-views-plugin/common'; +import { + METRIC_ID, + XY_ID, + METRIC_TREND_LINE_ID, + XY_DATA_ID, + XY_REFERENCE_LINE_ID, +} from './constants'; +import type { XYVisualOptions } from './xy_chart'; +import type { MetricLayerConfig, XYDataLayerConfig, XYReferenceLinesLayerConfig } from './layers'; export { XYChart, type XYVisualOptions } from './xy_chart'; export { MetricChart } from './metric_chart'; - export * from './layers'; +export type XYLayerConfig = XYDataLayerConfig | XYReferenceLinesLayerConfig; + +interface ChartModelBase { + id: string; + title?: string; + dataView?: DataView; +} +export interface XYChartModel extends ChartModelBase { + visualOptions?: XYVisualOptions; + visualizationType: typeof XY_ID; + layers: XYLayerConfig[]; +} +export interface MetricChartModel extends ChartModelBase { + visualizationType: typeof METRIC_ID; + layers: MetricLayerConfig; +} + +export type ChartModel = XYChartModel | MetricChartModel; +export type ChartTypes = typeof XY_ID | typeof METRIC_ID; +export { METRIC_ID, XY_ID, METRIC_TREND_LINE_ID, XY_DATA_ID, XY_REFERENCE_LINE_ID }; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts index a9680c27b764e..2620d7639f5ff 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts @@ -7,7 +7,7 @@ */ export { MetricLayer, type MetricLayerOptions, type MetricLayerConfig } from './metric_layer'; -export { XYDataLayer, type XYLayerOptions, type XYLayerConfig } from './xy_data_layer'; +export { XYDataLayer, type XYLayerOptions, type XYDataLayerConfig } from './xy_data_layer'; export { XYReferenceLinesLayer, type XYReferenceLinesLayerConfig, diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/metric_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/metric_layer.ts index 0a98f31aec158..a78191ffb82a7 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/metric_layer.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/metric_layer.ts @@ -16,6 +16,7 @@ import type { } from '@kbn/lens-plugin/public'; import type { ChartColumn, ChartLayer, FormulaValueConfig } from '../../types'; import { getDefaultReferences, getHistogramColumn } from '../../utils'; +import { METRIC_TREND_LINE_ID } from '../constants'; import { FormulaColumn } from './columns/formula'; const HISTOGRAM_COLUMN_NAME = 'x_date_histogram'; @@ -30,6 +31,7 @@ export interface MetricLayerOptions { export interface MetricLayerConfig { data: FormulaValueConfig; options?: MetricLayerOptions; + layerType?: typeof METRIC_TREND_LINE_ID; /** * It is possible to define a specific dataView for the layer. It will override the global chart one **/ @@ -38,8 +40,13 @@ export interface MetricLayerConfig { export class MetricLayer implements ChartLayer { private column: ChartColumn; - constructor(private layerConfig: MetricLayerConfig) { + private layerConfig: MetricLayerConfig; + constructor(layerConfig: MetricLayerConfig) { this.column = new FormulaColumn(layerConfig.data); + this.layerConfig = { + ...layerConfig, + layerType: layerConfig.layerType ?? 'metricTrendline', + }; } getLayer( diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_data_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_data_layer.ts index f4845a5c93ab9..d42a00e963484 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_data_layer.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_data_layer.ts @@ -12,7 +12,7 @@ import type { FormulaPublicApi, FormBasedPersistedState, PersistedIndexPatternLayer, - XYDataLayerConfig, + XYDataLayerConfig as LensXYDataLayerConfig, SeriesType, TermsIndexPatternColumn, DateHistogramIndexPatternColumn, @@ -26,6 +26,7 @@ import { type TopValuesColumnParams, type DateHistogramColumnParams, } from '../../utils'; +import { XY_DATA_ID } from '../constants'; import { FormulaColumn } from './columns/formula'; const BREAKDOWN_COLUMN_NAME = 'aggs_breakdown'; @@ -50,22 +51,25 @@ export interface XYLayerOptions { seriesType?: SeriesType; } -export interface XYLayerConfig { +export interface XYDataLayerConfig { data: FormulaValueConfig[]; options?: XYLayerOptions; + layerType?: typeof XY_DATA_ID; + /** * It is possible to define a specific dataView for the layer. It will override the global chart one **/ dataView?: DataView; } -export class XYDataLayer implements ChartLayer { +export class XYDataLayer implements ChartLayer { private column: ChartColumn[]; - private layerConfig: XYLayerConfig; - constructor(layerConfig: XYLayerConfig) { + private layerConfig: XYDataLayerConfig; + constructor(layerConfig: XYDataLayerConfig) { this.column = layerConfig.data.map((dataItem) => new FormulaColumn(dataItem)); this.layerConfig = { ...layerConfig, + layerType: layerConfig.layerType ?? 'data', options: { ...layerConfig.options, buckets: { @@ -151,7 +155,7 @@ export class XYDataLayer implements ChartLayer { return getDefaultReferences(this.layerConfig.dataView ?? chartDataView, layerId); } - getLayerConfig(layerId: string, accessorId: string): XYDataLayerConfig { + getLayerConfig(layerId: string, accessorId: string): LensXYDataLayerConfig { return { layerId, seriesType: this.layerConfig.options?.seriesType ?? 'line', diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_reference_lines_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_reference_lines_layer.ts index 2a105c10677af..bc2132f17918c 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_reference_lines_layer.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_reference_lines_layer.ts @@ -15,10 +15,12 @@ import type { } from '@kbn/lens-plugin/public'; import type { ChartLayer, StaticValueConfig, StaticChartColumn } from '../../types'; import { getDefaultReferences } from '../../utils'; +import { XY_REFERENCE_LINE_ID } from '../constants'; import { StaticColumn } from './columns/static'; export interface XYReferenceLinesLayerConfig { data: StaticValueConfig[]; + layerType?: typeof XY_REFERENCE_LINE_ID; /** * It is possible to define a specific dataView for the layer. It will override the global chart one **/ @@ -27,8 +29,13 @@ export interface XYReferenceLinesLayerConfig { export class XYReferenceLinesLayer implements ChartLayer { private column: StaticChartColumn[]; - constructor(private layerConfig: XYReferenceLinesLayerConfig) { + private layerConfig: XYReferenceLinesLayerConfig; + constructor(layerConfig: XYReferenceLinesLayerConfig) { this.column = layerConfig.data.map((p) => new StaticColumn(p)); + this.layerConfig = { + ...layerConfig, + layerType: layerConfig.layerType ?? 'referenceLine', + }; } getName(): string | undefined { diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/metric_chart.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/metric_chart.ts index 9c3f5bf3afe98..02b44c061878b 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/metric_chart.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/metric_chart.ts @@ -11,6 +11,7 @@ import type { SavedObjectReference } from '@kbn/core/server'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { Chart, ChartConfig, ChartLayer } from '../types'; import { DEFAULT_LAYER_ID } from '../utils'; +import { METRIC_ID } from './constants'; const ACCESSOR = 'metric_formula_accessor'; @@ -18,7 +19,7 @@ export class MetricChart implements Chart { constructor(private chartConfig: ChartConfig>) {} getVisualizationType(): string { - return 'lnsMetric'; + return METRIC_ID; } getLayers(): FormBasedPersistedState['layers'] { diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/xy_chart.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/xy_chart.ts index 6e0390ffe9e10..e0b42c2dcfbf5 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/xy_chart.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/xy_chart.ts @@ -7,7 +7,9 @@ */ import type { + AxisExtentConfig, FormBasedPersistedState, + LegendConfig, XYArgs, XYLayerConfig, XYState, @@ -17,6 +19,7 @@ import type { SavedObjectReference } from '@kbn/core/server'; import { AxesSettingsConfig } from '@kbn/visualizations-plugin/common'; import type { Chart, ChartConfig, ChartLayer } from '../types'; import { DEFAULT_LAYER_ID } from '../utils'; +import { XY_ID } from './constants'; const ACCESSOR = 'formula_accessor'; @@ -28,6 +31,8 @@ export interface XYVisualOptions { showDottedLine?: boolean; valueLabels?: XYArgs['valueLabels']; axisTitlesVisibilitySettings?: AxesSettingsConfig; + legend?: LegendConfig; + yLeftExtent?: AxisExtentConfig; } export class XYChart implements Chart { @@ -39,7 +44,7 @@ export class XYChart implements Chart { ) {} getVisualizationType(): string { - return 'lnsXY'; + return XY_ID; } private get layers() { @@ -79,12 +84,23 @@ export class XYChart implements Chart { }), ], }), + legend: this.chartConfig.visualOptions?.legend ?? { + isVisible: false, + position: 'right', + showSingleSeries: false, + }, fittingFunction: this.chartConfig.visualOptions?.missingValues ?? 'None', endValue: this.chartConfig.visualOptions?.endValues, curveType: this.chartConfig.visualOptions?.lineInterpolation, emphasizeFitting: !this.chartConfig.visualOptions?.showDottedLine, valueLabels: this.chartConfig.visualOptions?.valueLabels, - axisTitlesVisibilitySettings: this.chartConfig.visualOptions?.axisTitlesVisibilitySettings, + axisTitlesVisibilitySettings: this.chartConfig.visualOptions + ?.axisTitlesVisibilitySettings ?? { + x: false, + yLeft: false, + yRight: true, + }, + yLeftExtent: this.chartConfig.visualOptions?.yLeftExtent, }; } @@ -117,11 +133,6 @@ export const getXYVisualizationState = ( }, valueLabels: 'show', yLeftScale: 'linear', - axisTitlesVisibilitySettings: { - x: false, - yLeft: false, - yRight: true, - }, tickLabelsVisibilitySettings: { x: true, yLeft: true, diff --git a/packages/kbn-lens-embeddable-utils/index.ts b/packages/kbn-lens-embeddable-utils/index.ts index ddd5744f230b5..ffe9d4e87d788 100644 --- a/packages/kbn-lens-embeddable-utils/index.ts +++ b/packages/kbn-lens-embeddable-utils/index.ts @@ -12,9 +12,14 @@ export type { MetricLayerOptions, MetricLayerConfig, XYLayerOptions, - XYLayerConfig, + XYDataLayerConfig, XYReferenceLinesLayerConfig, XYVisualOptions, + XYLayerConfig, + ChartTypes, + ChartModel, + XYChartModel, + MetricChartModel, } from './attribute_builder/visualization_types'; export { @@ -25,6 +30,11 @@ export { XYChart, XYDataLayer, XYReferenceLinesLayer, + METRIC_ID, + METRIC_TREND_LINE_ID, + XY_ID, + XY_DATA_ID, + XY_REFERENCE_LINE_ID, } from './attribute_builder/visualization_types'; export { LensAttributesBuilder } from './attribute_builder/lens_attributes_builder'; diff --git a/packages/kbn-lens-embeddable-utils/kibana.jsonc b/packages/kbn-lens-embeddable-utils/kibana.jsonc index 9dc67508d99e9..4f8fd44b1ea65 100644 --- a/packages/kbn-lens-embeddable-utils/kibana.jsonc +++ b/packages/kbn-lens-embeddable-utils/kibana.jsonc @@ -1,5 +1,5 @@ { - "type": "shared-browser", + "type": "shared-common", "id": "@kbn/lens-embeddable-utils", "owner": "@elastic/obs-ux-infra_services-team" } diff --git a/packages/kbn-mock-idp-plugin/common/constants.ts b/packages/kbn-mock-idp-plugin/common/constants.ts new file mode 100644 index 0000000000000..6fb5475587574 --- /dev/null +++ b/packages/kbn-mock-idp-plugin/common/constants.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { resolve } from 'path'; + +export const MOCK_IDP_PLUGIN_PATH = resolve(__dirname, '..'); +export const MOCK_IDP_METADATA_PATH = resolve(MOCK_IDP_PLUGIN_PATH, 'metadata.xml'); + +export const MOCK_IDP_LOGIN_PATH = '/mock_idp/login'; +export const MOCK_IDP_LOGOUT_PATH = '/mock_idp/logout'; + +export const MOCK_IDP_REALM_NAME = 'mock-idp'; +export const MOCK_IDP_ENTITY_ID = 'urn:mock-idp'; // Must match `entityID` in `metadata.xml` +export const MOCK_IDP_ROLE_MAPPING_NAME = 'mock-idp-mapping'; + +export const MOCK_IDP_ATTRIBUTE_PRINCIPAL = 'http://saml.elastic-cloud.com/attributes/principal'; +export const MOCK_IDP_ATTRIBUTE_ROLES = 'http://saml.elastic-cloud.com/attributes/roles'; +export const MOCK_IDP_ATTRIBUTE_EMAIL = 'http://saml.elastic-cloud.com/attributes/email'; +export const MOCK_IDP_ATTRIBUTE_NAME = 'http://saml.elastic-cloud.com/attributes/name'; diff --git a/packages/kbn-mock-idp-plugin/common/index.ts b/packages/kbn-mock-idp-plugin/common/index.ts new file mode 100644 index 0000000000000..aaaffc15f10f8 --- /dev/null +++ b/packages/kbn-mock-idp-plugin/common/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { + MOCK_IDP_PLUGIN_PATH, + MOCK_IDP_METADATA_PATH, + MOCK_IDP_LOGIN_PATH, + MOCK_IDP_LOGOUT_PATH, + MOCK_IDP_REALM_NAME, + MOCK_IDP_ENTITY_ID, + MOCK_IDP_ROLE_MAPPING_NAME, + MOCK_IDP_ATTRIBUTE_PRINCIPAL, + MOCK_IDP_ATTRIBUTE_ROLES, + MOCK_IDP_ATTRIBUTE_EMAIL, + MOCK_IDP_ATTRIBUTE_NAME, +} from './constants'; +export { + createMockIdpMetadata, + createSAMLResponse, + ensureSAMLRoleMapping, + parseSAMLAuthnRequest, +} from './utils'; diff --git a/packages/kbn-mock-idp-plugin/common/utils.ts b/packages/kbn-mock-idp-plugin/common/utils.ts new file mode 100644 index 0000000000000..5d55fbc565685 --- /dev/null +++ b/packages/kbn-mock-idp-plugin/common/utils.ts @@ -0,0 +1,231 @@ +/* + * Copyright 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 { Client } from '@elastic/elasticsearch'; +import { SignedXml } from 'xml-crypto'; +import { KBN_KEY_PATH, KBN_CERT_PATH } from '@kbn/dev-utils'; +import { readFile } from 'fs/promises'; +import zlib from 'zlib'; +import { promisify } from 'util'; +import { parseString } from 'xml2js'; +import { X509Certificate } from 'crypto'; + +import { + MOCK_IDP_REALM_NAME, + MOCK_IDP_ENTITY_ID, + MOCK_IDP_ROLE_MAPPING_NAME, + MOCK_IDP_ATTRIBUTE_PRINCIPAL, + MOCK_IDP_ATTRIBUTE_ROLES, + MOCK_IDP_ATTRIBUTE_EMAIL, + MOCK_IDP_ATTRIBUTE_NAME, + MOCK_IDP_LOGIN_PATH, + MOCK_IDP_LOGOUT_PATH, +} from './constants'; + +const inflateRawAsync = promisify(zlib.inflateRaw); +const parseStringAsync = promisify(parseString); + +/** + * Creates XML metadata for our mock identity provider. + * + * This can be saved to file and used to configure Elasticsearch SAML realm. + * + * @param kibanaUrl Fully qualified URL where Kibana is hosted (including base path) + */ +export async function createMockIdpMetadata(kibanaUrl: string) { + const signingKey = await readFile(KBN_CERT_PATH); + const cert = new X509Certificate(signingKey); + const trimTrailingSlash = (url: string) => (url.endsWith('/') ? url.slice(0, -1) : url); + + return ` + + + + + + ${cert.raw.toString('base64')} + + + + + + + + + + `; +} + +/** + * Creates a SAML response that can be passed directly to the Kibana ACS endpoint to authenticate a user. + * + * @example Create a SAML response. + * + * ```ts + * const samlResponse = await createSAMLResponse({ + * username: '1234567890', + * email: 'mail@elastic.co', + * fullname: 'Test User', + * roles: ['t1_analyst', 'editor'], + * }) + * ``` + * + * @example Authenticate user with SAML response. + * + * ```ts + * fetch('/api/security/saml/callback', { + * method: 'POST', + * body: JSON.stringify({ SAMLResponse: samlResponse }), + * redirect: 'manual' + * }) + * ``` + */ +export async function createSAMLResponse(options: { + /** Fully qualified URL where Kibana is hosted (including base path) */ + kibanaUrl: string; + /** ID from SAML authentication request */ + authnRequestId?: string; + username: string; + fullname?: string; + email?: string; + roles: string[]; +}) { + const issueInstant = new Date().toISOString(); + const notOnOrAfter = new Date(Date.now() + 3600 * 1000).toISOString(); + + const samlAssertionTemplateXML = ` + + ${MOCK_IDP_ENTITY_ID} + + _643ec1b3f5673583b9f9a1e9e73a36daa2a3748f + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + + + + + ${options.username} + + + ${options.roles + .map( + (role) => `${role}` + ) + .join('')} + + ${ + options.email + ? ` + ${options.email} + ` + : '' + } + ${ + options.fullname + ? ` + ${options.fullname} + ` + : '' + } + + + `; + + const signature = new SignedXml(); + signature.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; + signature.signingKey = await readFile(KBN_KEY_PATH); + + // Adds a reference to a `Assertion` xml element and an array of transform algorithms to be used during signing. + signature.addReference( + `//*[local-name(.)='Assertion']`, + [ + 'http://www.w3.org/2000/09/xmldsig#enveloped-signature', + 'http://www.w3.org/2001/10/xml-exc-c14n#', + ], + 'http://www.w3.org/2001/04/xmlenc#sha256' + ); + + signature.computeSignature(samlAssertionTemplateXML, { + location: { reference: `//*[local-name(.)='Issuer']`, action: 'after' }, + }); + + const value = await Buffer.from( + ` + + ${MOCK_IDP_ENTITY_ID} + + + ${signature.getSignedXml()} + + ` + ).toString('base64'); + + return value; +} + +/** + * Creates the role mapping required for developers to authenticate using SAML. + */ +export async function ensureSAMLRoleMapping(client: Client) { + return client.transport.request({ + method: 'PUT', + path: `/_security/role_mapping/${MOCK_IDP_ROLE_MAPPING_NAME}`, + body: { + enabled: true, + role_templates: [ + { + template: '{"source":"{{#tojson}}groups{{/tojson}}"}', + format: 'json', + }, + ], + rules: { + all: [ + { + field: { + 'realm.name': MOCK_IDP_REALM_NAME, + }, + }, + ], + }, + }, + }); +} + +interface SAMLAuthnRequest { + 'saml2p:AuthnRequest': { + $: { + AssertionConsumerServiceURL: string; + Destination: string; + ID: string; + IssueInstant: string; + }; + }; +} + +export async function parseSAMLAuthnRequest(samlRequest: string) { + const inflatedSAMLRequest = (await inflateRawAsync(Buffer.from(samlRequest, 'base64'))) as Buffer; + const parsedSAMLRequest = (await parseStringAsync( + inflatedSAMLRequest.toString() + )) as SAMLAuthnRequest; + return parsedSAMLRequest['saml2p:AuthnRequest'].$; +} diff --git a/packages/kbn-mock-idp-plugin/kibana.jsonc b/packages/kbn-mock-idp-plugin/kibana.jsonc new file mode 100644 index 0000000000000..929d7b9b990db --- /dev/null +++ b/packages/kbn-mock-idp-plugin/kibana.jsonc @@ -0,0 +1,11 @@ +{ + "type": "plugin", + "id": "@kbn/mock-idp-plugin", + "owner": "@elastic/kibana-security", + "devOnly": true, + "plugin": { + "id": "mockIdpPlugin", + "server": true, + "browser": false + } +} \ No newline at end of file diff --git a/packages/kbn-subscription-tracking/package.json b/packages/kbn-mock-idp-plugin/package.json similarity index 69% rename from packages/kbn-subscription-tracking/package.json rename to packages/kbn-mock-idp-plugin/package.json index e9dd11b56c81b..456a2cfa5ce32 100644 --- a/packages/kbn-subscription-tracking/package.json +++ b/packages/kbn-mock-idp-plugin/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/subscription-tracking", + "name": "@kbn/mock-idp-plugin", "private": true, "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0" diff --git a/packages/kbn-mock-idp-plugin/server/index.ts b/packages/kbn-mock-idp-plugin/server/index.ts new file mode 100644 index 0000000000000..db807851d4564 --- /dev/null +++ b/packages/kbn-mock-idp-plugin/server/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 { plugin } from './plugin'; diff --git a/packages/kbn-mock-idp-plugin/server/plugin.ts b/packages/kbn-mock-idp-plugin/server/plugin.ts new file mode 100644 index 0000000000000..20c115d80cf2c --- /dev/null +++ b/packages/kbn-mock-idp-plugin/server/plugin.ts @@ -0,0 +1,120 @@ +/* + * Copyright 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 { PluginInitializer, Plugin } from '@kbn/core-plugins-server'; +import { schema } from '@kbn/config-schema'; + +import { + MOCK_IDP_LOGIN_PATH, + MOCK_IDP_LOGOUT_PATH, + createSAMLResponse, + parseSAMLAuthnRequest, +} from '../common'; + +export const plugin: PluginInitializer = async (): Promise => ({ + setup(core) { + core.http.resources.register( + { + path: MOCK_IDP_LOGIN_PATH, + validate: { + query: schema.object({ + SAMLRequest: schema.string(), + }), + }, + options: { authRequired: false }, + }, + async (context, request, response) => { + let samlRequest: Awaited>; + try { + samlRequest = await parseSAMLAuthnRequest(request.query.SAMLRequest); + } catch (error) { + return response.badRequest({ + body: '[request query.SAMLRequest]: value is not valid SAMLRequest.', + }); + } + + const userRoles: Array<[string, string]> = [ + ['system_indices_superuser', 'system_indices_superuser'], + ['t1_analyst', 't1_analyst'], + ['t2_analyst', 't2_analyst'], + ['t3_analyst', 't3_analyst'], + ['threat_intelligence_analyst', 'threat_intelligence_analyst'], + ['rule_author', 'rule_author'], + ['soc_manager', 'soc_manager'], + ['detections_admin', 'detections_admin'], + ['platform_engineer', 'platform_engineer'], + ['endpoint_operations_analyst', 'endpoint_operations_analyst'], + ['endpoint_policy_manager', 'endpoint_policy_manager'], + ]; + + const samlResponses = await Promise.all( + userRoles.map(([username, role]) => + createSAMLResponse({ + authnRequestId: samlRequest.ID, + kibanaUrl: samlRequest.AssertionConsumerServiceURL, + username, + roles: [role], + }) + ) + ); + + return response.renderHtml({ + body: ` + + Mock Identity Provider + + +

Mock Identity Provider

+
+

Pick a role:

+
    + ${userRoles + .map( + ([username], i) => + ` +
  • + +
  • + ` + ) + .join('')} +
+ + + `, + }); + } + ); + + core.http.resources.register( + { + path: `${MOCK_IDP_LOGIN_PATH}/submit.js`, + validate: false, + options: { authRequired: false }, + }, + (context, request, response) => { + return response.renderJs({ body: 'document.getElementById("loginForm").submit();' }); + } + ); + + core.http.resources.register( + { + path: MOCK_IDP_LOGOUT_PATH, + validate: false, + options: { authRequired: false }, + }, + async (context, request, response) => { + return response.redirected({ headers: { location: '/' } }); + } + ); + }, + start() {}, + stop() {}, +}); diff --git a/packages/kbn-mock-idp-plugin/tsconfig.json b/packages/kbn-mock-idp-plugin/tsconfig.json new file mode 100644 index 0000000000000..1420a34208f13 --- /dev/null +++ b/packages/kbn-mock-idp-plugin/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core-plugins-server", + "@kbn/config-schema", + "@kbn/dev-utils" + ] +} diff --git a/packages/kbn-openapi-bundler/README.md b/packages/kbn-openapi-bundler/README.md new file mode 100644 index 0000000000000..4a82cb9c20339 --- /dev/null +++ b/packages/kbn-openapi-bundler/README.md @@ -0,0 +1,493 @@ +# OpenAPI Specs Bundler for Kibana + +`@kbn/openapi-bundler` is a tool for transforming multiple OpenAPI specification files (source specs) into a single bundled specification file (target spec). +This can be used for API docs generation purposes. This approach allows you to: + +- Abstract away the knowledge of where you keep your OpenAPI specs, how many specs there are, and how to find them. The Docs team should only know where a single file is located - the bundle. +- Omit internal API endpoints from the bundle. +- Omit API endpoints that are hidden behind a feature flag and haven't been released yet. +- Omit parts of schemas that are hidden behind a feature flag (e.g. a new property added to an existing response schema). +- Omit custom OpenAPI attributes from the bundle, such as `x-codegen-enabled`, `x-internal`, and `x-modify` (see below). +- Transform the target schema according to the custom OpenAPI attributes, such as `x-modify`. +- Resolve references and inline some of them for better readability. The bundled file contains only local references and paths. + +## Getting started + +To let this package help you with bundling your OpenAPI specifications you should have OpenAPI specification describing your API endpoint request and response schemas along with common types used in your API. Refer [@kbn/openapi-generator](../kbn-openapi-generator/README.md) and [OpenAPI 3.0.3](https://swagger.io/specification/v3/) (support for [OpenAPI 3.1.0](https://swagger.io/specification/) is planned to be added soon) for more details. + +Following recommendations provided in `@kbn/openapi-generator` you should have OpenAPI specs defined under a common folder something like `my-plugin/common/api`. + +Currently package supports only programmatic API. As the next step you need to create a JavaScript script file like below and put it to `my-plugin/scripts/openapi` + +```ts +require('../../../../../src/setup_node_env'); +const { bundle } = require('@kbn/openapi-bundler'); +const { resolve } = require('path'); + +// define ROOT as `my-plugin` instead of `my-plugin/scripts/openapi` +// pay attention to this constant when your script's location is different +const ROOT = resolve(__dirname, '../..'); + +bundle({ + rootDir: ROOT, // Root path e.g. plugin root directory + sourceGlob: './**/*.schema.yaml', // Glob pattern to find OpenAPI specification files + outputFilePath: './target/openapi/my-plugin.bundled.schema.yaml', // +}); +``` + +And add a script entry to your `package.json` file + +```json +{ + "author": "Elastic", + ... + "scripts": { + ... + "openapi:bundle": "node scripts/openapi/bundle" + } +} +``` + +Finally you should be able to run OpenAPI bundler via + +```bash +yarn openapi:bundle +``` + +This command will produce a bundled file `my-plugin/target/openapi/my-plugin.bundled.schema.yaml` containing +all specs matching `./**/*.schema.yaml` glob pattern. + +Here's an example how your source schemas can look like and the expected result + +- `example1.schema.yaml` + +```yaml +openapi: 3.0.3 +info: + title: My endpoint + version: '2023-10-31' + +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +- `example2.schema.yaml` + +```yaml +openapi: 3.0.3 +info: + title: My endpoint + version: '2023-10-31' + +paths: + /api/path/to/endpoint: + post: + x-internal: true + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +And the target spec will look like + +```yaml +openapi: 3.0.3 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + post: + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +components: + schemas: {} +``` + +## Supported custom (`x-` prefixed) properties + +OpenAPI specification allows to define custom properties. They can be used to describe extra functionality that is not covered by the standard OpenAPI Specification. We currently support the following custom properties + +- [x-internal](#x-internal) - marks source spec nodes the bundler must NOT include in the target spec +- [x-modify](#x-modify) - marks nodes to be modified by the bundler +- [x-inline](#x-inline) - marks reference nodes to be inlined when bundled + +### `x-internal` + +Marks source spec nodes the bundler must NOT include in the target spec. + +**Supported values**: `true` + +When bundler encounters a node with `x-internal: true` it doesn't include this node into the target spec. It's useful when it's necessary to hide some chunk of OpenAPI spec because functionality supporting it is hidden under a feature flag or the chunk is just for internal use. + +#### Examples + +The following spec defines an API endpoint `/api/path/to/endpoint` accepting `GET` and `POST` requests. It has `x-internal: true` defined in `post` section meaning it won't be included in the target spec. + +```yaml +openapi: 3.0.3 +info: + title: My endpoint + version: '2023-10-31' + +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + post: + x-internal: true + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +The target spec will look like + +```yaml +openapi: 3.0.3 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +`x-internal: true` can also be defined next to a reference. + +```yaml +openapi: 3.0.3 +info: + title: My endpoint + version: '2023-10-31' + +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + post: + $ref: '#/components/schemas/MyPostEndpointResponse' + x-internal: true + +components: + schemas: + MyPostEndpointResponse: + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +The target spec will look like + +```yaml +openapi: 3.0.3 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + get: + operationId: MyGetEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +### `x-modify` + +Marks nodes to be modified by the bundler. + +**Supported values**: `partial` or `required` + +Value `partial` leads to removing `required` property making params under `properties` optional. Value `required` leads to adding or extending `required` property by adding all param names under `properties`. + +#### Examples + +The following spec has `x-modify: partial` at `schema` section. It makes params optional for a PATCH request. + +```yaml +openapi: 3.0.0 +info: + title: My endpoint + version: '2023-10-31' +paths: + /api/path/to/endpoint: + patch: + operationId: MyPatchEndpoint + requestBody: + required: true + content: + application/json: + schema: + x-modify: partial + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number + required: + - param1 + - param2 +``` + +The target spec will look like + +```yaml +openapi: 3.0.0 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + patch: + operationId: MyPatchEndpoint + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number +``` + +The following spec has `x-modify: required` at `schema` section. It makes params optional for a PATCH request. + +```yaml +openapi: 3.0.0 +info: + title: My endpoint + version: '2023-10-31' +paths: + /api/path/to/endpoint: + put: + operationId: MyPutEndpoint + requestBody: + required: true + content: + application/json: + schema: + x-modify: required + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number +``` + +The target spec will look like + +```yaml +openapi: 3.0.0 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + patch: + operationId: MyPatchEndpoint + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number + required: + - param1 + - param2 +``` + +`x-modify` can also be defined next to a reference. + +```yaml +openapi: 3.0.0 +info: + title: My endpoint + version: '2023-10-31' +paths: + /api/path/to/endpoint: + patch: + operationId: MyPatchEndpoint + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatchProps' + x-modify: partial + +components: + schemas: + PatchProps: + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number + required: + - param1 + - param2 +``` + +The target spec will look like + +```yaml +openapi: 3.0.0 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + patch: + operationId: MyPatchEndpoint + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + param1: + type: string + enum: [val1, val2, val3] + param2: + type: number +``` + +### `x-inline` + +Marks reference nodes to be inlined when bundled. + +**Supported values**: `true` + +`x-inline: true` can be specified at a reference node itself (a node with `$ref` key) or at a node `$ref` resolves to. When bundler encounters such a node it assigns (copies keys via `Object.assign()`) the latter node (a node`$ref` resolves to) to the first node (a node with `$ref` key). This way target won't have referenced component in `components` as well. + +#### Examples + +The following spec defines an API endpoint `/api/path/to/endpoint` accepting `POST` request. It has `x-inline: true` specified in `post` section meaning reference `#/components/schemas/MyPostEndpointResponse` will be inlined in the target spec. + +```yaml +openapi: 3.0.3 +info: + title: My endpoint + version: '2023-10-31' + +paths: + /api/path/to/endpoint: + post: + $ref: '#/components/schemas/MyPostEndpointResponse' + x-inline: true + +components: + schemas: + MyPostEndpointResponse: + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` + +The target spec will look like + +```yaml +openapi: 3.0.3 +info: + title: Bundled specs file. See individual paths.verb.tags for details + version: not applicable +paths: + /api/path/to/endpoint: + post: + operationId: MyPostEndpoint + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object +``` diff --git a/packages/kbn-openapi-bundler/index.ts b/packages/kbn-openapi-bundler/index.ts new file mode 100644 index 0000000000000..badd58def955e --- /dev/null +++ b/packages/kbn-openapi-bundler/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/openapi_bundler'; diff --git a/packages/kbn-subscription-tracking/jest.config.js b/packages/kbn-openapi-bundler/jest.config.js similarity index 87% rename from packages/kbn-subscription-tracking/jest.config.js rename to packages/kbn-openapi-bundler/jest.config.js index edc1839850dae..a1a9647773313 100644 --- a/packages/kbn-subscription-tracking/jest.config.js +++ b/packages/kbn-openapi-bundler/jest.config.js @@ -9,5 +9,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../..', - roots: ['/packages/kbn-subscription-tracking'], + roots: ['/packages/kbn-openapi-bundler'], }; diff --git a/packages/kbn-openapi-bundler/kibana.jsonc b/packages/kbn-openapi-bundler/kibana.jsonc new file mode 100644 index 0000000000000..0434322591cc6 --- /dev/null +++ b/packages/kbn-openapi-bundler/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "devOnly": true, + "id": "@kbn/openapi-bundler", + "owner": "@elastic/security-detection-rule-management", + "type": "shared-common" +} diff --git a/packages/kbn-openapi-bundler/package.json b/packages/kbn-openapi-bundler/package.json new file mode 100644 index 0000000000000..83d92beb71aa1 --- /dev/null +++ b/packages/kbn-openapi-bundler/package.json @@ -0,0 +1,7 @@ +{ + "description": "OpenAPI specs bundler for Kibana", + "license": "SSPL-1.0 OR Elastic License 2.0", + "name": "@kbn/openapi-bundler", + "private": true, + "version": "1.0.0" +} diff --git a/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/expected.yaml new file mode 100644 index 0000000000000..d8eb6a8b66c68 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/expected.yaml @@ -0,0 +1,40 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/ConflictTestSchema' + +spec2.schema.yaml: + openapi: 3.0.3 + info: + title: Another test endpoint + version: '2023-10-31' + paths: + /api/another_api: + put: + operationId: AnotherTestEndpointPut + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/ConflictTestSchema' + +shared_components.schema.yaml: + components: + schemas: + ConflictTestSchema: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec1.schema.yaml new file mode 100644 index 0000000000000..a44cd371ba326 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec1.schema.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ConflictTestSchema' + +components: + schemas: + ConflictTestSchema: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec2.schema.yaml new file mode 100644 index 0000000000000..4a5670f8ae5f5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/conflicting_but_equal_refs_in_different_specs/spec2.schema.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.3 +info: + title: Another test endpoint + version: '2023-10-31' +paths: + /api/another_api: + put: + operationId: AnotherTestEndpointPut + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ConflictTestSchema' + +components: + schemas: + ConflictTestSchema: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec1.schema.yaml new file mode 100644 index 0000000000000..765811b78a619 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec1.schema.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ConflictTestSchema' + +components: + schemas: + ConflictTestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec2.schema.yaml new file mode 100644 index 0000000000000..4a5670f8ae5f5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/conflicting_refs_in_different_specs/spec2.schema.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.3 +info: + title: Another test endpoint + version: '2023-10-31' +paths: + /api/another_api: + put: + operationId: AnotherTestEndpointPut + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ConflictTestSchema' + +components: + schemas: + ConflictTestSchema: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/expected.yaml new file mode 100644 index 0000000000000..0b916b7b17ac2 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/expected.yaml @@ -0,0 +1,44 @@ +version1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + +version2.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-11-11' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + field2: + type: string + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version1.schema.yaml new file mode 100644 index 0000000000000..5b7f6a8718fcf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version1.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer diff --git a/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version2.schema.yaml new file mode 100644 index 0000000000000..4492f449ba2fe --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_endpoint_versions/version2.schema.yaml @@ -0,0 +1,20 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-11-11' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + field2: + type: string diff --git a/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/expected.yaml new file mode 100644 index 0000000000000..3aa6c7051ebcf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/expected.yaml @@ -0,0 +1,42 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + +spec2.schema.yaml: + openapi: 3.1.0 + info: + title: Test endpoint POST + version: '2023-10-31' + paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field2: + type: string + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec1.schema.yaml new file mode 100644 index 0000000000000..5b7f6a8718fcf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec1.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer diff --git a/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec2.schema.yaml new file mode 100644 index 0000000000000..e437e40e6698e --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/different_openapi_versions/spec2.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.1.0 +info: + title: Test endpoint POST + version: '2023-10-31' +paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field2: + type: string diff --git a/packages/kbn-openapi-bundler/src/__test__/inline_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/inline_ref/expected.yaml new file mode 100644 index 0000000000000..270886caa051e --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/inline_ref/expected.yaml @@ -0,0 +1,50 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + anyOf: + - type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + - $ref: './shared_components.schema.yaml#/components/schemas/TestSchema2' + - $ref: './shared_components.schema.yaml#/components/schemas/TestSchema3' + +shared_components.schema.yaml: + components: + schemas: + TestSchema2: + x-inline: false + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + + TestSchema3: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/inline_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/inline_ref/spec.schema.yaml new file mode 100644 index 0000000000000..f5cdb2694a5c8 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/inline_ref/spec.schema.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/TestSchema1' + - $ref: '#/components/schemas/TestSchema2' + - $ref: '#/components/schemas/TestSchema3' + +components: + schemas: + TestSchema1: + x-inline: true + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + + TestSchema2: + x-inline: false + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + + TestSchema3: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/expected.yaml new file mode 100644 index 0000000000000..fa679d0c3f8c0 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/expected.yaml @@ -0,0 +1,26 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/spec.schema.yaml new file mode 100644 index 0000000000000..9646051aab907 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_partial_node/spec.schema.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + x-modify: partial + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + required: + - field1 + - field2 diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/expected.yaml new file mode 100644 index 0000000000000..fa679d0c3f8c0 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/expected.yaml @@ -0,0 +1,26 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/spec.schema.yaml new file mode 100644 index 0000000000000..547bb4cd913be --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_partial_ref/spec.schema.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/TestSchema' + x-modify: partial + +components: + schemas: + TestSchema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + required: + - field1 + - field2 diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_required_node/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_required_node/expected.yaml new file mode 100644 index 0000000000000..3b075e3cf803b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_required_node/expected.yaml @@ -0,0 +1,29 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + required: + - field1 + - field2 + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_required_node/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_required_node/spec.schema.yaml new file mode 100644 index 0000000000000..68d478ea8caaa --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_required_node/spec.schema.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + x-modify: required + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/expected.yaml new file mode 100644 index 0000000000000..3b075e3cf803b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/expected.yaml @@ -0,0 +1,29 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + required: + - field1 + - field2 + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/spec.schema.yaml new file mode 100644 index 0000000000000..0f02e3e905e23 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/modify_required_ref/spec.schema.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/TestSchema' + x-modify: required + +components: + schemas: + TestSchema: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/common.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/common.schema.yaml new file mode 100644 index 0000000000000..6e0ec47b773c5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/common.schema.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: {} + +components: + schemas: + CircularTestSchema: + type: string + data: + items: + $ref: '#/components/schemas/AnotherCircularTestSchema' + + AnotherCircularTestSchema: + anyof: + - $ref: '#/components/schemas/CircularTestSchema' + - type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/expected.yaml new file mode 100644 index 0000000000000..cce50159ca39f --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/expected.yaml @@ -0,0 +1,50 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/CircularTestSchema' + +spec2.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint POST + version: '2023-10-31' + paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/CircularTestSchema' + +shared_components.schema.yaml: + components: + schemas: + CircularTestSchema: + type: string + data: + items: + $ref: '#/components/schemas/AnotherCircularTestSchema' + + AnotherCircularTestSchema: + anyof: + - $ref: '#/components/schemas/CircularTestSchema' + - type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec1.schema.yaml new file mode 100644 index 0000000000000..2e64f53087f84 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec1.schema.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './common.schema.yaml#/components/schemas/CircularTestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec2.schema.yaml new file mode 100644 index 0000000000000..92ebc5f4468e5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_ref_specs/spec2.schema.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Test endpoint POST + version: '2023-10-31' +paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './common.schema.yaml#/components/schemas/CircularTestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_spec/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_spec/expected.yaml new file mode 100644 index 0000000000000..b5bb0cffb6390 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_spec/expected.yaml @@ -0,0 +1,27 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: &ref0 + type: object + properties: + - name: field1 + required: false + schema: *ref0 + - field2: + required: false + schema: + type: string + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/recursive_spec/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/recursive_spec/spec.schema.yaml new file mode 100644 index 0000000000000..f9e3d8b2c590e --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/recursive_spec/spec.schema.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: &ref0 + type: object + properties: + - name: field1 + required: false + schema: *ref0 + - field2: + required: false + schema: + type: string diff --git a/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/expected.yaml new file mode 100644 index 0000000000000..2b75720a069b1 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/expected.yaml @@ -0,0 +1,24 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/CircularTestSchema' + +shared_components.schema.yaml: + components: + schemas: + CircularTestSchema: + type: string + data: + $ref: '#/components/schemas/CircularTestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/spec.schema.yaml new file mode 100644 index 0000000000000..d90a117818455 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/self_recursive_ref/spec.schema.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/CircularTestSchema' + +components: + schemas: + CircularTestSchema: + type: string + data: + $ref: '#/components/schemas/CircularTestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/skip_internal/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/skip_internal/expected.yaml new file mode 100644 index 0000000000000..d8f6d6cb474bf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/skip_internal/expected.yaml @@ -0,0 +1,31 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + anyOf: + - $ref: './shared_components.schema.yaml#/components/schemas/TestSchema1' + - type: object + +shared_components.schema.yaml: + components: + schemas: + TestSchema1: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/skip_internal/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/skip_internal/spec.schema.yaml new file mode 100644 index 0000000000000..1d172978a4240 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/skip_internal/spec.schema.yaml @@ -0,0 +1,57 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/TestSchema1' + - $ref: '#/components/schemas/TestSchema2' + x-internal: true + - type: object + properties: + x-internal: true + field1: + $ref: '#/components/schemas/TestSchema3' + +components: + schemas: + TestSchema1: + # x-internal is not supported here + # x-internal: true + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + + TestSchema2: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 + + TestSchema3: + type: object + properties: + field1: + type: string + enum: [value1] + field2: + type: integer + minimum: 1 diff --git a/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/expected.yaml new file mode 100644 index 0000000000000..3015eb607287a --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/expected.yaml @@ -0,0 +1,22 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec1.schema.yaml new file mode 100644 index 0000000000000..5b7f6a8718fcf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec1.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer diff --git a/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec2.schema.yaml new file mode 100644 index 0000000000000..5a53977b69100 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/skip_internal_endpoint/spec2.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint POST + version: '2023-10-31' +paths: + /internal/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field2: + type: string diff --git a/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/common.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/common.schema.yaml new file mode 100644 index 0000000000000..b710c4e8b114b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/common.schema.yaml @@ -0,0 +1,13 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: {} + +components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/expected.yaml new file mode 100644 index 0000000000000..48c9045d62ce5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/expected.yaml @@ -0,0 +1,25 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/TestSchema' + +shared_components.schema.yaml: + components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/spec.schema.yaml new file mode 100644 index 0000000000000..b1d910fa5e963 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/spec_with_external_ref/spec.schema.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './common.schema.yaml#/components/schemas/TestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/expected.yaml new file mode 100644 index 0000000000000..48c9045d62ce5 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/expected.yaml @@ -0,0 +1,25 @@ +spec.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/TestSchema' + +shared_components.schema.yaml: + components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/spec.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/spec.schema.yaml new file mode 100644 index 0000000000000..2339d5eb7aa59 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/spec_with_local_ref/spec.schema.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/TestSchema' + +components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/expected.yaml new file mode 100644 index 0000000000000..dbe9fdb445e86 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/expected.yaml @@ -0,0 +1,42 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer + +spec2.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint POST + version: '2023-10-31' + paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field2: + type: string + +shared_components.schema.yaml: + components: {} diff --git a/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec1.schema.yaml new file mode 100644 index 0000000000000..5b7f6a8718fcf --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec1.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field1: + type: integer diff --git a/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec2.schema.yaml new file mode 100644 index 0000000000000..c3ba67e0b46ea --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_simple_specs/spec2.schema.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.3 +info: + title: Test endpoint POST + version: '2023-10-31' +paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + field2: + type: string diff --git a/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/common.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/common.schema.yaml new file mode 100644 index 0000000000000..b710c4e8b114b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/common.schema.yaml @@ -0,0 +1,13 @@ +openapi: 3.0.3 +info: + title: Test endpoint + version: '2023-10-31' +paths: {} + +components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/expected.yaml b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/expected.yaml new file mode 100644 index 0000000000000..d3040ae511b2c --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/expected.yaml @@ -0,0 +1,42 @@ +spec1.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint GET + version: '2023-10-31' + paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/TestSchema' + +spec2.schema.yaml: + openapi: 3.0.3 + info: + title: Test endpoint POST + version: '2023-10-31' + paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './shared_components.schema.yaml#/components/schemas/TestSchema' + +shared_components.schema.yaml: + components: + schemas: + TestSchema: + type: string + enum: + - value1 + - value2 diff --git a/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec1.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec1.schema.yaml new file mode 100644 index 0000000000000..c08570d69311c --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec1.schema.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Test endpoint GET + version: '2023-10-31' +paths: + /api/some_api: + get: + operationId: TestEndpointGet + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './common.schema.yaml#/components/schemas/TestSchema' diff --git a/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec2.schema.yaml b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec2.schema.yaml new file mode 100644 index 0000000000000..9dec5566875bb --- /dev/null +++ b/packages/kbn-openapi-bundler/src/__test__/two_specs_with_external_ref/spec2.schema.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Test endpoint POST + version: '2023-10-31' +paths: + /api/some_api: + post: + operationId: TestEndpointPost + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: './common.schema.yaml#/components/schemas/TestSchema' diff --git a/packages/kbn-openapi-bundler/src/bundler/__mocks__/ref_resolver.ts b/packages/kbn-openapi-bundler/src/bundler/__mocks__/ref_resolver.ts new file mode 100644 index 0000000000000..69fd3f5a41af4 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/__mocks__/ref_resolver.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 const RefResolver = jest.fn().mockImplementation(() => ({ + resolveRef: jest.fn(), + resolveDocument: jest.fn(), +})); diff --git a/packages/kbn-openapi-bundler/src/bundler/bundle_document.ts b/packages/kbn-openapi-bundler/src/bundler/bundle_document.ts new file mode 100644 index 0000000000000..1f6884a87f677 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/bundle_document.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { isAbsolute } from 'path'; +import { RefResolver } from './ref_resolver'; +import { processDocument } from './process_document'; +import { BundleRefProcessor } from './document_processors/bundle_refs'; +import { createSkipNodeWithInternalPropProcessor } from './document_processors/skip_node_with_internal_prop'; +import { createModifyPartialProcessor } from './document_processors/modify_partial'; +import { createSkipInternalPathProcessor } from './document_processors/skip_internal_path'; +import { ResolvedDocument, ResolvedRef } from './types'; +import { createRemovePropsProcessor } from './document_processors/remove_props'; +import { createModifyRequiredProcessor } from './document_processors/modify_required'; +import { X_CODEGEN_ENABLED, X_INLINE, X_INTERNAL, X_MODIFY } from './known_custom_props'; +import { RemoveUnusedComponentsProcessor } from './document_processors/remove_unused_components'; +import { isPlainObjectType } from '../utils/is_plain_object_type'; + +export class SkipException extends Error { + constructor(public documentPath: string, message: string) { + super(message); + } +} + +export interface BundledDocument extends ResolvedDocument { + bundledRefs: ResolvedRef[]; +} + +/** + * Bundles document into one file and performs appropriate document modifications. + * + * Bundling assumes external references defined via `$ref` are included into the result document. + * Some of the references get inlined. + * + * Document modification includes the following + * - skips nodes with `x-internal: true` property + * - skips paths started with `/internal` + * - modifies nodes having `x-modify` + * + * @param absoluteDocumentPath document's absolute path + * @returns bundled document + */ +export async function bundleDocument(absoluteDocumentPath: string): Promise { + if (!isAbsolute(absoluteDocumentPath)) { + throw new Error( + `bundleDocument expects an absolute document path but got "${absoluteDocumentPath}"` + ); + } + + const refResolver = new RefResolver(); + const resolvedDocument = await refResolver.resolveDocument(absoluteDocumentPath); + + if (!hasPaths(resolvedDocument.document as MaybeObjectWithPaths)) { + // Specs without paths defined are usually considered as shared. Such specs have `components` defined + // and referenced by the specs with paths defined. In this case the shared specs have been + // handled already and must be skipped. + // + // An additional case when it's a rogue spec. Rogue specs are skipped as well as they don't contribute + // to the API endpoints. + throw new SkipException(resolvedDocument.absolutePath, 'Document has no paths defined'); + } + + const bundleRefsProcessor = new BundleRefProcessor(X_INLINE); + const removeUnusedComponentsProcessor = new RemoveUnusedComponentsProcessor(); + + await processDocument(resolvedDocument, refResolver, [ + createSkipNodeWithInternalPropProcessor(X_INTERNAL), + createSkipInternalPathProcessor('/internal'), + createModifyPartialProcessor(), + createModifyRequiredProcessor(), + createRemovePropsProcessor([X_MODIFY, X_CODEGEN_ENABLED]), + bundleRefsProcessor, + removeUnusedComponentsProcessor, + ]); + + if (isPlainObjectType(resolvedDocument.document.components)) { + removeUnusedComponentsProcessor.removeUnusedComponents(resolvedDocument.document.components); + } + + // If document.paths were removed by processors skip the document + if (!hasPaths(resolvedDocument.document as MaybeObjectWithPaths)) { + throw new SkipException( + resolvedDocument.absolutePath, + 'Document has no paths after processing the document' + ); + } + + return { ...resolvedDocument, bundledRefs: bundleRefsProcessor.getBundledRefs() }; +} + +interface MaybeObjectWithPaths { + paths?: unknown; +} + +function hasPaths(document: MaybeObjectWithPaths): boolean { + return ( + typeof document.paths === 'object' && + document.paths !== null && + Object.keys(document.paths).length > 0 + ); +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/bundle_refs.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/bundle_refs.ts new file mode 100644 index 0000000000000..4064e92a7b806 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/bundle_refs.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Document, ResolvedRef, TraverseDocumentContext, RefNode } from '../types'; +import { hasProp } from '../../utils/has_prop'; +import { isChildContext } from '../is_child_context'; +import { inlineRef } from './utils/inline_ref'; +import { insertRefByPointer } from '../../utils/insert_by_json_pointer'; + +/** + * Node processor to bundle and conditionally dereference document references. + * + * Bundling means all external references like `../../some_file.schema.yaml#/components/schemas/SomeSchema` saved + * to the result document under corresponding path `components` -> `schemas` -> `SomeSchema` and `$ref` property's + * values is updated to `#/components/schemas/SomeSchema`. + * + * Conditional dereference means inlining references when `inliningPredicate()` returns `true`. If `inliningPredicate` + * is not passed only bundling happens. + */ +export class BundleRefProcessor { + private refs: ResolvedRef[] = []; + + constructor(private inliningPropName: string) {} + + ref(node: RefNode, resolvedRef: ResolvedRef, context: TraverseDocumentContext): void { + if (!resolvedRef.pointer.startsWith('/components/schemas')) { + throw new Error(`$ref pointer must start with "/components/schemas"`); + } + + if ( + hasProp(node, this.inliningPropName, true) || + hasProp(resolvedRef.refNode, this.inliningPropName, true) + ) { + inlineRef(node, resolvedRef); + + delete node[this.inliningPropName]; + } else { + const rootDocument = this.extractRootDocument(context); + + if (!rootDocument.components) { + rootDocument.components = {}; + } + + node.$ref = this.saveComponent( + resolvedRef, + rootDocument.components as Record + ); + this.refs.push(resolvedRef); + } + } + + getBundledRefs(): ResolvedRef[] { + return this.refs; + } + + private saveComponent(ref: ResolvedRef, components: Record): string { + insertRefByPointer(ref.pointer, ref.refNode, components); + + return `#${ref.pointer}`; + } + + private extractRootDocument(context: TraverseDocumentContext): Document { + while (isChildContext(context)) { + context = context.parentContext; + } + + return context.resolvedDocument.document; + } +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_partial.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_partial.ts new file mode 100644 index 0000000000000..13c876b7579ca --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_partial.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 { DocumentNodeProcessor } from '../types'; +import { hasProp } from '../../utils/has_prop'; +import { inlineRef } from './utils/inline_ref'; +import { X_MODIFY } from '../known_custom_props'; + +/** + * Creates a node processor to modify a node by removing `required` property when + * `x-modify: partial` property is presented in the node. + */ +export function createModifyPartialProcessor(): DocumentNodeProcessor { + return { + ref(node, resolvedRef) { + if (!hasProp(node, X_MODIFY, 'partial')) { + return; + } + + // Inline the ref node because we are gonna modify it + inlineRef(node, resolvedRef); + + delete node.required; + }, + leave(node) { + if (!hasProp(node, X_MODIFY, 'partial')) { + return; + } + + delete node.required; + }, + }; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_required.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_required.ts new file mode 100644 index 0000000000000..14a9ac2ea25c6 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/modify_required.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 chalk from 'chalk'; +import { logger } from '../../logger'; +import { isPlainObjectType } from '../../utils/is_plain_object_type'; +import { DocumentNodeProcessor } from '../types'; +import { hasProp } from '../../utils/has_prop'; +import { X_MODIFY } from '../known_custom_props'; +import { inlineRef } from './utils/inline_ref'; + +/** + * Creates a node processor to modify a node by add or extending `required` property + * when `x-modify: required` property is presented in the node. + */ +export function createModifyRequiredProcessor(): DocumentNodeProcessor { + return { + ref(node, resolvedRef) { + if (!hasProp(node, X_MODIFY, 'required')) { + return; + } + + if (!hasProp(resolvedRef.refNode, 'properties')) { + logger.warning( + `Unable to apply ${chalk.blueBright(X_MODIFY)} to ${chalk.cyan( + resolvedRef.pointer + )} because ${chalk.blueBright('properties')} property was not found` + ); + return; + } + + if (!isPlainObjectType(resolvedRef.refNode.properties)) { + logger.warning( + `Unable to apply ${chalk.blueBright(X_MODIFY)} to ${chalk.cyan( + resolvedRef.pointer + )} because ${chalk.blueBright('properties')} property was not an object` + ); + return; + } + + // Inline the ref node because we are gonna modify it + inlineRef(node, resolvedRef); + + node.required = Object.keys(resolvedRef.refNode.properties); + }, + leave(node) { + if (!hasProp(node, X_MODIFY, 'required')) { + return; + } + + if (!hasProp(node, 'properties')) { + logger.warning( + `Unable to apply ${chalk.blueBright(X_MODIFY)} to ${chalk.cyan( + node + )} because ${chalk.blueBright('properties')} property was not found` + ); + return; + } + + if (!isPlainObjectType(node.properties)) { + logger.warning( + `Unable to apply ${chalk.blueBright(X_MODIFY)} to ${chalk.cyan( + node + )} because ${chalk.blueBright('properties')} property was not an object` + ); + return; + } + + node.required = Object.keys(node.properties); + }, + }; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_props.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_props.ts new file mode 100644 index 0000000000000..616d9db11f55e --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_props.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { isPlainObjectType } from '../../utils/is_plain_object_type'; +import { DocumentNodeProcessor } from '../types'; + +/** + * Creates a node processor to remove specified by `propNames` properties. + */ +export function createRemovePropsProcessor(propNames: string[]): DocumentNodeProcessor { + return { + leave(node) { + if (!isPlainObjectType(node)) { + return; + } + + for (const propName of propNames) { + if (!node[propName]) { + continue; + } + + delete node[propName]; + } + }, + }; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_unused_components.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_unused_components.ts new file mode 100644 index 0000000000000..1f5053d4667fe --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/remove_unused_components.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 { hasProp } from '../../utils/has_prop'; +import { isPlainObjectType } from '../../utils/is_plain_object_type'; +import { PlainObjectNode, ResolvedRef } from '../types'; + +/** + * Helps to remove unused components. + * + * To achieve it requires including in document processors list to collect encountered refs + * and then `removeUnusedComponents()` should be invoked after document processing to perform + * actual unused components deletion. + */ +export class RemoveUnusedComponentsProcessor { + private refs = new Set(); + + ref(node: unknown, resolvedRef: ResolvedRef): void { + // If the reference has been inlined by one of the previous processors skip it + if (!hasProp(node, '$ref')) { + return; + } + + this.refs.add(resolvedRef.pointer); + } + + removeUnusedComponents(components: PlainObjectNode): void { + if (!isPlainObjectType(components.schemas)) { + return; + } + + for (const schema of Object.keys(components.schemas)) { + if (!this.refs.has(`/components/schemas/${schema}`)) { + delete components.schemas[schema]; + } + } + } +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/skip_internal_path.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/skip_internal_path.ts new file mode 100644 index 0000000000000..42769eab7a68a --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/skip_internal_path.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { DocumentNodeProcessor } from '../types'; + +/** + * Creates a node processor to skip paths starting with `/internal` and omit them from the result document. + */ +export function createSkipInternalPathProcessor(skipPathPrefix: string): DocumentNodeProcessor { + return { + enter(_, context) { + if (typeof context.parentKey === 'number') { + return false; + } + + return context.parentKey.startsWith(skipPathPrefix); + }, + }; +} diff --git a/packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/skip_node_with_internal_prop.ts similarity index 50% rename from packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts rename to packages/kbn-openapi-bundler/src/bundler/document_processors/skip_node_with_internal_prop.ts index a332a21acb431..4931036bcd1bc 100644 --- a/packages/kbn-ui-actions-browser/src/triggers/categorize_field_trigger.ts +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/skip_node_with_internal_prop.ts @@ -6,11 +6,16 @@ * Side Public License, v 1. */ -import { Trigger } from './trigger'; +import { DocumentNodeProcessor } from '../types'; -export const CATEGORIZE_FIELD_TRIGGER = 'CATEGORIZE_FIELD_TRIGGER'; -export const categorizeFieldTrigger: Trigger = { - id: CATEGORIZE_FIELD_TRIGGER, - title: 'Run pattern analysis', - description: 'Triggered when user wants to run pattern analysis on a field.', -}; +/** + * Creates a node processor to skip nodes having provided `skipProperty` property + * and omit them from the result document. + */ +export function createSkipNodeWithInternalPropProcessor( + skipProperty: string +): DocumentNodeProcessor { + return { + enter: (node) => skipProperty in node, + }; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/types.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/types.ts new file mode 100644 index 0000000000000..60fd512c3cf5b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/types.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface InlinableRefNode { + $ref?: string; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/document_processors/utils/inline_ref.ts b/packages/kbn-openapi-bundler/src/bundler/document_processors/utils/inline_ref.ts new file mode 100644 index 0000000000000..3106bf9cbc95d --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/document_processors/utils/inline_ref.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 { cloneDeep } from 'lodash'; +import { DocumentNode, ResolvedRef } from '../../types'; +import { InlinableRefNode } from '../types'; + +export function inlineRef(node: DocumentNode, resolvedRef: ResolvedRef): void { + // Make sure unwanted side effects don't happen when child nodes are processed + const deepClone = cloneDeep(resolvedRef.refNode); + + Object.assign(node, deepClone); + + delete (node as InlinableRefNode).$ref; +} diff --git a/packages/kbn-subscription-tracking/src/helpers.ts b/packages/kbn-openapi-bundler/src/bundler/is_child_context.ts similarity index 57% rename from packages/kbn-subscription-tracking/src/helpers.ts rename to packages/kbn-openapi-bundler/src/bundler/is_child_context.ts index 251c0d1c04116..f3fe4dde915cf 100644 --- a/packages/kbn-subscription-tracking/src/helpers.ts +++ b/packages/kbn-openapi-bundler/src/bundler/is_child_context.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ -import type { SubscriptionContextData } from '../types'; +import { TraverseChildDocumentContext, TraverseDocumentContext } from './types'; -const sourceStringRegEx = /^(\w[\w\-_]*)__(\w[\w\-_]*)$/; -export function isValidContext(context: SubscriptionContextData): boolean { - return context.feature.length > 0 && sourceStringRegEx.test(context.source); +export function isChildContext( + context: TraverseDocumentContext +): context is TraverseChildDocumentContext { + return 'parentContext' in context; } diff --git a/packages/kbn-openapi-bundler/src/bundler/known_custom_props.ts b/packages/kbn-openapi-bundler/src/bundler/known_custom_props.ts new file mode 100644 index 0000000000000..4a1832a62bce0 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/known_custom_props.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * `x-internal: true` marks nodes the bundler must NOT include in the result bundled document. Any other values are ignored. + */ +export const X_INTERNAL = 'x-internal'; + +/** + * `x-internal: true` marks reference nodes the bundler must inline in the result bundled document. + */ +export const X_INLINE = 'x-inline'; + +/** + * `x-modify` marks nodes to be modified by the bundler. `partial` and `required` values are supported. + * + * - `partial` leads to removing `required` property making params under `properties` optional + * - `required` leads to adding or extending `required` property by adding all param names under `properties` + */ +export const X_MODIFY = 'x-modify'; + +/** + * `x-codegen-enabled` is used by the code generator package `@kbn/openapi-generator` and shouldn't be included + * in result bundled document. + */ +export const X_CODEGEN_ENABLED = 'x-codegen-enabled'; diff --git a/packages/kbn-openapi-bundler/src/bundler/merge_documents.ts b/packages/kbn-openapi-bundler/src/bundler/merge_documents.ts new file mode 100644 index 0000000000000..e27253cefc1c9 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/merge_documents.ts @@ -0,0 +1,142 @@ +/* + * Copyright 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 deepEqual from 'fast-deep-equal'; +import { basename, dirname, join } from 'path'; +import chalk from 'chalk'; +import { parseRef } from '../utils/parse_ref'; +import { insertRefByPointer } from '../utils/insert_by_json_pointer'; +import { DocumentNodeProcessor, PlainObjectNode, ResolvedDocument, ResolvedRef } from './types'; +import { BundledDocument } from './bundle_document'; +import { processDocument } from './process_document'; + +type MergedDocuments = Record; + +type MergedResult = Record; + +const SHARED_COMPONENTS_FILE_NAME = 'shared_components.schema.yaml'; + +export async function mergeDocuments(bundledDocuments: BundledDocument[]): Promise { + const mergedDocuments: MergedDocuments = {}; + const componentsMap = new Map(); + + for (const bundledDocument of bundledDocuments) { + mergeRefsToMap(bundledDocument.bundledRefs, componentsMap); + + delete bundledDocument.document.components; + + await setRefsFileName(bundledDocument, SHARED_COMPONENTS_FILE_NAME); + mergeDocument(bundledDocument, mergedDocuments); + } + + const result: MergedResult = {}; + + for (const fileName of Object.keys(mergedDocuments)) { + result[fileName] = mergedDocuments[fileName].document; + } + + result[SHARED_COMPONENTS_FILE_NAME] = { + components: componentsMapToComponents(componentsMap), + }; + + return result; +} + +function mergeDocument(resolvedDocument: ResolvedDocument, mergeResult: MergedDocuments): void { + const fileName = basename(resolvedDocument.absolutePath); + + if (!mergeResult[fileName]) { + mergeResult[fileName] = resolvedDocument; + return; + } + + const nonConflictFileName = generateNonConflictingFilePath( + resolvedDocument.absolutePath, + mergeResult + ); + + mergeResult[nonConflictFileName] = resolvedDocument; +} + +function generateNonConflictingFilePath( + documentAbsolutePath: string, + mergeResult: MergedDocuments +): string { + let pathToDocument = dirname(documentAbsolutePath); + let suggestedName = basename(documentAbsolutePath); + + while (mergeResult[suggestedName]) { + suggestedName = `${basename(pathToDocument)}_${suggestedName}`; + pathToDocument = join(pathToDocument, '..'); + } + + return suggestedName; +} + +function mergeRefsToMap(bundledRefs: ResolvedRef[], componentsMap: Map): void { + for (const bundledRef of bundledRefs) { + const existingRef = componentsMap.get(bundledRef.pointer); + + if (!existingRef) { + componentsMap.set(bundledRef.pointer, bundledRef); + continue; + } + + if (deepEqual(existingRef.refNode, bundledRef.refNode)) { + continue; + } + + throw new Error( + `❌ Unable to bundle documents due to conflicts in references. Schema ${chalk.yellow( + bundledRef.pointer + )} is defined in ${chalk.blue(existingRef.absolutePath)} and in ${chalk.magenta( + bundledRef.absolutePath + )} but has not matching definitions.` + ); + } +} + +function componentsMapToComponents( + componentsMap: Map +): Record { + const result: Record = {}; + + for (const resolvedRef of componentsMap.values()) { + insertRefByPointer(resolvedRef.pointer, resolvedRef.refNode, result); + } + + return result; +} + +async function setRefsFileName( + resolvedDocument: ResolvedDocument, + fileName: string +): Promise { + // We don't need to follow references + const stubRefResolver = { + resolveRef: async (refDocumentAbsolutePath: string, pointer: string): Promise => ({ + absolutePath: refDocumentAbsolutePath, + pointer, + document: resolvedDocument.document, + refNode: {}, + }), + resolveDocument: async (): Promise => ({ + absolutePath: '', + document: resolvedDocument.document, + }), + }; + const setRefFileProcessor: DocumentNodeProcessor = { + ref: (node) => { + const { pointer } = parseRef(node.$ref); + + node.$ref = `./${fileName}#${pointer}`; + }, + }; + + await processDocument(resolvedDocument, stubRefResolver, [setRefFileProcessor]); +} diff --git a/packages/kbn-openapi-bundler/src/bundler/process_document.test.ts b/packages/kbn-openapi-bundler/src/bundler/process_document.test.ts new file mode 100644 index 0000000000000..d78a4ce515b65 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/process_document.test.ts @@ -0,0 +1,224 @@ +/* + * Copyright 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 { processDocument } from './process_document'; +import { RefResolver } from './ref_resolver'; +import { Document, DocumentNodeProcessor } from './types'; + +jest.mock('./ref_resolver'); + +describe('processDocument', () => { + it('invokes processors in the provided order', async () => { + const resolvedDocument = { + absolutePath: '/path/to/document', + document: {} as Document, + }; + const calls: string[] = []; + const processor1 = { + leave() { + calls.push('processor1'); + }, + }; + const processor2 = { + leave() { + calls.push('processor2'); + }, + }; + + processDocument(resolvedDocument, new RefResolver(), [processor1, processor2]); + + expect(calls).toEqual(['processor1', 'processor2']); + }); + + it('invokes callbacks in expected order (enter -> ref -> leave)', async () => { + const document = { + id: 'root', + t1: { + id: 't1', + $ref: '#/TestRef', + }, + }; + const calls: string[] = []; + const refResolver = new RefResolver(); + const processor: DocumentNodeProcessor = { + enter(node) { + calls.push(`enter - ${(node as NodeWithId).id}`); + return false; + }, + ref(node) { + calls.push(`ref - ${(node as NodeWithId).id}`); + }, + leave(node) { + calls.push(`leave - ${(node as NodeWithId).id}`); + }, + }; + + const refNode = { + id: 'TestRef', + bar: 'foo', + }; + + (refResolver.resolveRef as jest.Mock).mockResolvedValue({ + absolutePath: '/path/to/document', + document: { + TestRef: refNode, + }, + refNode, + pointer: '/TestRef', + }); + + await processDocument( + { + absolutePath: '/path/to/document', + document: document as unknown as Document, + }, + refResolver, + [processor] + ); + + expect(calls).toEqual([ + 'enter - root', + 'enter - t1', + 'enter - TestRef', + 'leave - TestRef', + 'ref - t1', + 'leave - t1', + 'leave - root', + ]); + }); + + it('removes a node after "enter" callback returned true', async () => { + const nodeToRemove = { + id: 't2', + foo: 'bar', + }; + const document = { + t1: { + id: 't1', + }, + t2: nodeToRemove, + }; + const removeNodeProcessor: DocumentNodeProcessor = { + enter(node) { + return node === nodeToRemove; + }, + }; + + await processDocument( + { + absolutePath: '/path/to/document', + document: document as unknown as Document, + }, + new RefResolver(), + [removeNodeProcessor] + ); + + expect(document).toEqual({ + t1: { + id: 't1', + }, + }); + }); + + it('handles recursive documents', async () => { + const nodeA: Record = { + foo: 'bar', + }; + const nodeB: Record = { + bar: ' foo', + }; + + nodeA.circular = nodeB; + nodeB.circular = nodeA; + + const document = { + nodeA, + nodeB, + }; + + await processDocument( + { + absolutePath: '/path/to/document', + document: document as unknown as Document, + }, + new RefResolver(), + [] + ); + + expect(document).toBeDefined(); + }); + + it('handles self-recursive references', async () => { + const document = { + node: { + $ref: '#/TestComponentCircular', + }, + TestComponentCircular: { + $ref: '#/TestComponentCircular', + }, + }; + const refResolver = new RefResolver(); + + (refResolver.resolveRef as jest.Mock).mockResolvedValue({ + absolutePath: '/path/to/document', + document, + refNode: { + $ref: '#/TestComponentCircular', + }, + pointer: '/TestComponentCircular', + }); + + await processDocument( + { + absolutePath: '/path/to/document', + document: document as unknown as Document, + }, + refResolver, + [] + ); + + expect(document).toBeDefined(); + }); + + it('handles recursive references', async () => { + const document: Record = { + node: { + $ref: '#/TestComponentCircular', + }, + TestComponentCircular: { + $ref: '#/AnotherTestComponentCircular', + }, + AnotherTestComponentCircular: { + $ref: '#/TestComponentCircular', + }, + }; + const refResolver = new RefResolver(); + + (refResolver.resolveRef as jest.Mock).mockImplementation((_, pointer) => ({ + absolutePath: '/path/to/document', + document, + refNode: document[pointer.slice(1)], + pointer, + })); + + await processDocument( + { + absolutePath: '/path/to/document', + document: document as unknown as Document, + }, + refResolver, + [] + ); + + expect(document).toBeDefined(); + }); +}); + +interface NodeWithId { + id?: string; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/process_document.ts b/packages/kbn-openapi-bundler/src/bundler/process_document.ts new file mode 100644 index 0000000000000..1efe64b87b4ed --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/process_document.ts @@ -0,0 +1,203 @@ +/* + * Copyright 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 { dirname } from 'path'; +import { isPlainObject } from 'lodash'; +import { IRefResolver } from './ref_resolver'; +import { + DocumentNode, + ResolvedDocument, + TraverseDocumentContext, + ResolvedRef, + DocumentNodeProcessor, + RefNode, + PlainObjectNode, +} from './types'; +import { parseRef } from '../utils/parse_ref'; +import { toAbsolutePath } from '../utils/to_absolute_path'; +import { isPlainObjectType } from '../utils/is_plain_object_type'; +import { isChildContext } from './is_child_context'; + +interface TraverseItem { + node: DocumentNode; + context: TraverseDocumentContext; + /** + * Keeps track of visited nodes to be able to detect circular references + */ + visitedDocumentNodes: Set; + parentNode: DocumentNode; + parentKey: string | number; + resolvedRef?: ResolvedRef; +} + +export async function processDocument( + resolvedDocument: ResolvedDocument, + refResolver: IRefResolver, + processors: DocumentNodeProcessor[] +): Promise { + const nodesToVisit: TraverseItem[] = [ + { + node: resolvedDocument.document, + context: { + resolvedDocument, + }, + visitedDocumentNodes: new Set(), + parentNode: resolvedDocument.document, + parentKey: '', + }, + ]; + const postOrderTraversalStack: TraverseItem[] = []; + + while (nodesToVisit.length > 0) { + const traverseItem = nodesToVisit.pop() as TraverseItem; + + if (!isTraversableNode(traverseItem.node)) { + continue; + } + + if (traverseItem.visitedDocumentNodes.has(traverseItem.node)) { + // Circular reference in the current document detected + continue; + } + + traverseItem.visitedDocumentNodes.add(traverseItem.node); + + if (shouldSkipNode(traverseItem, processors)) { + removeNode(traverseItem); + continue; + } + + postOrderTraversalStack.push(traverseItem); + + if (isRefNode(traverseItem.node)) { + const currentDocument = isChildContext(traverseItem.context) + ? traverseItem.context.resolvedRef + : traverseItem.context.resolvedDocument; + const { path, pointer } = parseRef(traverseItem.node.$ref); + const refAbsolutePath = path + ? toAbsolutePath(path, dirname(currentDocument.absolutePath)) + : currentDocument.absolutePath; + const absoluteRef = `${refAbsolutePath}#${pointer}`; + + if (isCircularRef(absoluteRef, traverseItem.context)) { + continue; + } + + const resolvedRef = await refResolver.resolveRef(refAbsolutePath, pointer); + const childContext = { + resolvedRef, + parentContext: traverseItem.context, + followedRef: absoluteRef, + }; + + traverseItem.resolvedRef = resolvedRef; + + nodesToVisit.push({ + node: resolvedRef.refNode, + context: childContext, + visitedDocumentNodes: new Set(), + parentNode: traverseItem.parentNode, + parentKey: traverseItem.parentKey, + }); + + continue; + } + + if (Array.isArray(traverseItem.node)) { + for (let i = 0; i < traverseItem.node.length; ++i) { + const nodeItem = traverseItem.node[i]; + + nodesToVisit.push({ + node: nodeItem as DocumentNode, + context: traverseItem.context, + visitedDocumentNodes: traverseItem.visitedDocumentNodes, + parentNode: traverseItem.node, + parentKey: i, + }); + } + } + + if (isPlainObjectType(traverseItem.node)) { + for (const key of Object.keys(traverseItem.node)) { + const value = traverseItem.node[key]; + + nodesToVisit.push({ + node: value as DocumentNode, + context: traverseItem.context, + visitedDocumentNodes: traverseItem.visitedDocumentNodes, + parentNode: traverseItem.node, + parentKey: key, + }); + } + } + } + + for (let i = postOrderTraversalStack.length - 1; i >= 0; --i) { + const traverseItem = postOrderTraversalStack[i]; + + for (const processor of processors) { + // If ref has been inlined by one of the processors it's not a ref node anymore + // so we can skip the following processors + if (isRefNode(traverseItem.node) && traverseItem.resolvedRef) { + processor.ref?.( + traverseItem.node as RefNode, + traverseItem.resolvedRef, + traverseItem.context + ); + } + + processor.leave?.(traverseItem.node, traverseItem.context); + } + } +} + +function isTraversableNode(maybeTraversableNode: unknown): boolean { + // We need to process only objects and arrays. Scalars pass through as is. + return typeof maybeTraversableNode === 'object' && maybeTraversableNode !== null; +} + +export function isRefNode(node: DocumentNode): node is { $ref: string } { + return isPlainObject(node) && '$ref' in node; +} + +function shouldSkipNode(traverseItem: TraverseItem, processors: DocumentNodeProcessor[]): boolean { + return processors?.some((p) => + p.enter?.(traverseItem.node, { + ...traverseItem.context, + parentNode: traverseItem.parentNode, + parentKey: traverseItem.parentKey, + }) + ); +} + +function removeNode(traverseItem: TraverseItem): void { + if (Array.isArray(traverseItem.parentNode) && typeof traverseItem.parentKey === 'number') { + traverseItem.parentNode.splice(traverseItem.parentKey, 1); + return; + } + + delete (traverseItem.parentNode as PlainObjectNode)[traverseItem.parentKey]; +} + +function isCircularRef(absoluteRef: string, context: TraverseDocumentContext): boolean { + let nextContext: TraverseDocumentContext | undefined = context; + + if (!isChildContext(nextContext)) { + return false; + } + + do { + if (nextContext.followedRef === absoluteRef) { + return true; + } + + nextContext = nextContext.parentContext; + } while (nextContext); + + return false; +} diff --git a/packages/kbn-openapi-bundler/src/bundler/ref_resolver.ts b/packages/kbn-openapi-bundler/src/bundler/ref_resolver.ts new file mode 100644 index 0000000000000..92b17c19c1b60 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/ref_resolver.ts @@ -0,0 +1,62 @@ +/* + * Copyright 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 { extractByJsonPointer } from '../utils/extract_by_json_pointer'; +import { readYamlDocument } from '../utils/read_yaml_document'; +import { ResolvedDocument, ResolvedRef } from './types'; + +export interface IRefResolver { + resolveRef(refDocumentAbsolutePath: string, pointer: string): Promise; + resolveDocument(documentAbsolutePath: string): Promise; +} + +export class RefResolver implements IRefResolver { + private documentsCache = new Map(); + + async resolveRef(refDocumentAbsolutePath: string, pointer: string): Promise { + const resolvedRefDocument = await this.resolveDocument(refDocumentAbsolutePath); + const refNode = extractByJsonPointer(resolvedRefDocument.document, pointer); + const resolvedRef = { + absolutePath: refDocumentAbsolutePath, + pointer, + document: resolvedRefDocument.document, + refNode, + }; + + return resolvedRef; + } + + async resolveDocument(documentAbsolutePath: string): Promise { + if (!path.isAbsolute(documentAbsolutePath)) { + throw new Error( + `resolveDocument requires absolute document path, provided path "${documentAbsolutePath}" is not absolute` + ); + } + + const cachedDocument = this.documentsCache.get(documentAbsolutePath); + + if (cachedDocument) { + return cachedDocument; + } + + try { + const document = await readYamlDocument(documentAbsolutePath); + const resolvedRef = { + absolutePath: documentAbsolutePath, + document, + }; + + this.documentsCache.set(documentAbsolutePath, resolvedRef); + + return resolvedRef; + } catch (e) { + throw new Error(`Unable to resolve document "${documentAbsolutePath}"`, { cause: e }); + } + } +} diff --git a/packages/kbn-openapi-bundler/src/bundler/types.ts b/packages/kbn-openapi-bundler/src/bundler/types.ts new file mode 100644 index 0000000000000..06aa533c9122a --- /dev/null +++ b/packages/kbn-openapi-bundler/src/bundler/types.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. + */ + +/** + * A plain object node not containing `$ref` property + */ +export type PlainObjectNode = Record; + +/** + * An array node + */ +export type ArrayNode = unknown[]; + +/** + * A ref node containing `$ref` property besides the others + */ +export interface RefNode extends PlainObjectNode { + $ref: string; +} + +/** + * An abstract OpenAPI entry node. Content besides $ref isn't important. + */ +export type DocumentNode = PlainObjectNode | ArrayNode | RefNode; + +/** + * Document abstraction. We don't mind OpenAPI `3.0` and `3.1` differences. + */ +export type Document = Record; + +export interface ResolvedDocument { + /** + * Document's absolute path + */ + absolutePath: string; + /** + * Document's root + */ + document: Document; +} + +export interface ResolvedRef extends ResolvedDocument { + /** + * Parsed pointer without leading hash symbol (e.g. `/components/schemas/MySchema`) + */ + pointer: string; + + /** + * Resolved ref's node pointer points to + */ + refNode: DocumentNode; +} + +export interface TraverseRootDocumentContext { + /** + * Root document + */ + resolvedDocument: ResolvedDocument; + + parentContext?: undefined; + followedRef?: undefined; +} + +export interface TraverseChildDocumentContext { + /** + * Current document after resolving $ref property + */ + resolvedRef: ResolvedRef; + + /** + * Context of the parent document the current one in `document` field was referenced via $ref. Empty if it's the root document. + */ + parentContext: TraverseDocumentContext; + + /** + * Reference used to resolve the current document + */ + followedRef: string; +} + +/** + * Traverse context storing additional information related to the currently traversed node + */ +export type TraverseDocumentContext = TraverseRootDocumentContext | TraverseChildDocumentContext; + +export type TraverseDocumentEntryContext = TraverseDocumentContext & { + parentNode: DocumentNode; + parentKey: string | number; +}; + +/** + * Entry processor controls when a node should be omitted from the result document. + * + * When result is `true` - omit the node. + */ +export type EntryProcessorFn = ( + node: Readonly, + context: TraverseDocumentEntryContext +) => boolean; + +export type LeaveProcessorFn = (node: DocumentNode, context: TraverseDocumentContext) => void; + +export type RefProcessorFn = ( + node: RefNode, + resolvedRef: ResolvedRef, + context: TraverseDocumentContext +) => void; + +/** + * Document or document node processor gives flexibility in modifying OpenAPI specs and/or collect some metrics. + * For convenience it defined handlers invoked upon action or specific node type. + * + * Currently the following node types supported + * + * - ref - Callback function is invoked upon leaving ref node (a node having `$ref` key) + * + * and the following actions + * + * - enter - Callback function is invoked upon entering any type of node element including ref nodes. It doesn't allow + * to modify node's content but provides an ability to remove the element by returning `true`. + * + * - leave - Callback function is invoked upon leaving any type of node. It give an opportunity to modify the document like + * dereference refs or remove unwanted properties. + */ +export interface DocumentNodeProcessor { + enter?: EntryProcessorFn; + leave?: LeaveProcessorFn; + ref?: RefProcessorFn; +} diff --git a/packages/kbn-openapi-bundler/src/logger.ts b/packages/kbn-openapi-bundler/src/logger.ts new file mode 100644 index 0000000000000..de3779fff8871 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/logger.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 { ToolingLog } from '@kbn/tooling-log'; + +export const logger = new ToolingLog({ + level: 'debug', + writeTo: process.stdout, +}); diff --git a/packages/kbn-openapi-bundler/src/openapi_bundler.test.ts b/packages/kbn-openapi-bundler/src/openapi_bundler.test.ts new file mode 100644 index 0000000000000..eaed80727dee8 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/openapi_bundler.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright 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 { existsSync, rmSync } from 'fs'; +import { basename, join } from 'path'; +import { bundle } from './openapi_bundler'; +import { readYamlDocument } from './utils/read_yaml_document'; + +const rootPath = join(__dirname, '__test__'); +const targetAbsoluteFilePath = join(rootPath, 'bundled.yaml'); + +describe('OpenAPI Bundler', () => { + afterEach(() => { + removeTargetFile(); + }); + + it('bundles two simple specs', async () => { + await bundleFolder('two_simple_specs'); + await expectBundleToMatchFile('two_simple_specs', 'expected.yaml'); + }); + + it('bundles one file with a local reference', async () => { + await bundleFolder('spec_with_local_ref'); + await expectBundleToMatchFile('spec_with_local_ref', 'expected.yaml'); + }); + + it('bundles one file with an external reference', async () => { + await bundleFolder('spec_with_external_ref'); + await expectBundleToMatchFile('spec_with_external_ref', 'expected.yaml'); + }); + + it('bundles files with external references', async () => { + await bundleFolder('two_specs_with_external_ref'); + await expectBundleToMatchFile('two_specs_with_external_ref', 'expected.yaml'); + }); + + // Fails because `writeYamlDocument()` has `noRefs: true` setting + // it('bundles recursive spec', async () => { + // await bundleFolder('recursive_spec'); + // await expectBundleToMatchFile('recursive_spec', 'expected.yaml'); + // }); + + it('bundles specs with recursive references', async () => { + await bundleFolder('recursive_ref_specs'); + await expectBundleToMatchFile('recursive_ref_specs', 'expected.yaml'); + }); + + it('bundles spec with a self-recursive reference', async () => { + await bundleFolder('self_recursive_ref'); + await expectBundleToMatchFile('self_recursive_ref', 'expected.yaml'); + }); + + it('bundles one endpoint with different versions', async () => { + await bundleFolder('different_endpoint_versions'); + await expectBundleToMatchFile('different_endpoint_versions', 'expected.yaml'); + }); + + it('bundles spec with different OpenAPI versions', async () => { + await bundleFolder('different_openapi_versions'); + await expectBundleToMatchFile('different_openapi_versions', 'expected.yaml'); + }); + + it('bundles conflicting but equal references', async () => { + await bundleFolder('conflicting_but_equal_refs_in_different_specs'); + await expectBundleToMatchFile('conflicting_but_equal_refs_in_different_specs', 'expected.yaml'); + }); + + it('fails to bundle conflicting references encountered in separate specs', async () => { + await expectBundlingError( + 'conflicting_refs_in_different_specs', + /\/components\/schemas\/ConflictTestSchema/ + ); + }); + + describe('x-modify', () => { + it('makes properties in an object node partial', async () => { + await bundleFolder('modify_partial_node'); + await expectBundleToMatchFile('modify_partial_node', 'expected.yaml'); + }); + + it('makes properties in a referenced object node partial', async () => { + await bundleFolder('modify_partial_ref'); + await expectBundleToMatchFile('modify_partial_ref', 'expected.yaml'); + }); + + it('makes properties in an object node required', async () => { + await bundleFolder('modify_required_node'); + await expectBundleToMatchFile('modify_required_node', 'expected.yaml'); + }); + + it('makes properties in a referenced object node required', async () => { + await bundleFolder('modify_required_ref'); + await expectBundleToMatchFile('modify_required_ref', 'expected.yaml'); + }); + }); + + describe('x-inline', () => { + it('inlines a reference', async () => { + await bundleFolder('inline_ref'); + await expectBundleToMatchFile('inline_ref', 'expected.yaml'); + }); + }); + + describe('skip internal', () => { + it('skips nodes with x-internal property', async () => { + await bundleFolder('skip_internal'); + await expectBundleToMatchFile('skip_internal', 'expected.yaml'); + }); + + it('skips endpoints starting with /internal', async () => { + await bundleFolder('skip_internal_endpoint'); + await expectBundleToMatchFile('skip_internal_endpoint', 'expected.yaml'); + }); + }); +}); + +async function bundleFolder(folderName: string): Promise { + await expect( + bundle({ + rootDir: join(rootPath, folderName), + sourceGlob: '*.schema.yaml', + outputFilePath: join('..', basename(targetAbsoluteFilePath)), + }) + ).resolves.toBeUndefined(); +} + +async function expectBundlingError( + folderName: string, + error: string | RegExp | jest.Constructable | Error | undefined +): Promise { + return await expect( + bundle({ + rootDir: join(rootPath, folderName), + sourceGlob: '*.schema.yaml', + outputFilePath: join('..', basename(targetAbsoluteFilePath)), + }) + ).rejects.toThrowError(error); +} + +async function expectBundleToMatchFile( + folderName: string, + expectedFileName: string +): Promise { + expect(existsSync(targetAbsoluteFilePath)).toBeTruthy(); + + const bundledSpec = await readYamlDocument(targetAbsoluteFilePath); + const expectedAbsoluteFilePath = join(rootPath, folderName, expectedFileName); + const expectedSpec = await readYamlDocument(expectedAbsoluteFilePath); + + expect(bundledSpec).toEqual(expectedSpec); +} + +function removeTargetFile(): void { + if (existsSync(targetAbsoluteFilePath)) { + rmSync(targetAbsoluteFilePath, { force: true }); + } +} diff --git a/packages/kbn-openapi-bundler/src/openapi_bundler.ts b/packages/kbn-openapi-bundler/src/openapi_bundler.ts new file mode 100644 index 0000000000000..451b0ff700bae --- /dev/null +++ b/packages/kbn-openapi-bundler/src/openapi_bundler.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import chalk from 'chalk'; +import globby from 'globby'; +import { basename, dirname, join, resolve } from 'path'; +import { BundledDocument, bundleDocument, SkipException } from './bundler/bundle_document'; +import { mergeDocuments } from './bundler/merge_documents'; +import { removeFilesByGlob } from './utils/remove_files_by_glob'; +import { logger } from './logger'; +import { writeYamlDocument } from './utils/write_yaml_document'; + +export interface BundlerConfig { + rootDir: string; + sourceGlob: string; + outputFilePath: string; +} + +export const bundle = async (config: BundlerConfig) => { + const { + rootDir, + sourceGlob, + outputFilePath: relativeOutputFilePath = 'target/openapi/bundled.schema.yaml', + } = config; + + logger.debug(chalk.bold(`Bundling API route schemas`)); + logger.debug(chalk.bold(`Working directory: ${chalk.underline(rootDir)}`)); + logger.debug(`👀 Searching for source files`); + + const outputFilePath = join(rootDir, relativeOutputFilePath); + const sourceFilesGlob = resolve(rootDir, sourceGlob); + const schemaFilePaths = await globby([sourceFilesGlob]); + + logger.info(`🕵️‍♀️ Found ${schemaFilePaths.length} schemas`); + logSchemas(schemaFilePaths); + + logger.info(`🧹 Cleaning up any previously generated artifacts`); + await removeFilesByGlob(dirname(outputFilePath), basename(outputFilePath)); + + logger.debug(`Processing schemas...`); + + const resolvedDocuments = await Promise.all( + schemaFilePaths.map(async (schemaFilePath) => { + try { + const resolvedDocument = await bundleDocument(schemaFilePath); + + logger.debug(`Processed ${chalk.bold(basename(schemaFilePath))}`); + + return resolvedDocument; + } catch (e) { + if (e instanceof SkipException) { + logger.info(`Skipped ${chalk.bold(e.documentPath)}: ${e.message}`); + return; + } + + throw e; + } + }) + ); + + const processedDocuments = filterOutSkippedDocuments(resolvedDocuments); + + logger.success(`Processed ${processedDocuments.length} schemas`); + + const resultDocument = await mergeDocuments(processedDocuments); + + try { + await writeYamlDocument(outputFilePath, resultDocument); + + logger.success(`📖 Wrote all bundled OpenAPI specs to ${chalk.bold(outputFilePath)}`); + } catch (e) { + logger.error(`Unable to save bundled document to ${chalk.bold(outputFilePath)}: ${e.message}`); + } +}; + +function logSchemas(schemaFilePaths: string[]): void { + for (const filePath of schemaFilePaths) { + logger.debug(`Found OpenAPI spec ${chalk.bold(filePath)}`); + } +} + +function filterOutSkippedDocuments( + documents: Array +): BundledDocument[] { + const processedDocuments: BundledDocument[] = []; + + for (const document of documents) { + if (!document) { + continue; + } + + processedDocuments.push(document); + } + + return processedDocuments; +} diff --git a/packages/kbn-openapi-bundler/src/utils/extract_by_json_pointer.ts b/packages/kbn-openapi-bundler/src/utils/extract_by_json_pointer.ts new file mode 100644 index 0000000000000..15507ac938ae1 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/extract_by_json_pointer.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { isPlainObjectType } from './is_plain_object_type'; + +/** + * Extract a node from a document using a provided [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901). + * + * JSON Pointer is the second part in [JSON Reference](https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03). + * For example an object `{ $ref: "./some-file.yaml#/components/schemas/MySchema"}` is a reference node. + * Where `/components/schemas/MySchema` is a JSON pointer. `./some-file.yaml` is a document reference. + * Yaml shares the same JSON reference standard and basically can be considered just as a different + * JS Object serialization format. See OpenAPI [Using $ref](https://swagger.io/docs/specification/using-ref/) for more information. + * + * @param document a document containing node to resolve by using the pointer + * @param pointer a JSON Pointer + * @returns resolved document node + */ +export function extractByJsonPointer(document: unknown, pointer: string): Record { + if (!pointer.startsWith('/')) { + throw new Error('$ref pointer must start with a leading slash'); + } + + if (!isPlainObjectType(document)) { + throw new Error('document must be an object'); + } + + let target = document; + + for (const segment of pointer.slice(1).split('/')) { + const nextTarget = target[segment]; + + if (!isPlainObjectType(nextTarget)) { + throw new Error(`JSON Pointer "${pointer}" is not found in "${JSON.stringify(document)}"`); + } + + target = nextTarget; + } + + return target; +} diff --git a/packages/kbn-openapi-bundler/src/utils/has_prop.ts b/packages/kbn-openapi-bundler/src/utils/has_prop.ts new file mode 100644 index 0000000000000..aa38041b8178d --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/has_prop.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 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 { isPlainObjectType } from './is_plain_object_type'; + +export function hasProp( + node: unknown, + propName: Property, + propValue?: Value +): node is { [key in Property]: Value } & Record { + if (!isPlainObjectType(node) || !(propName in node)) { + return false; + } + + return propValue ? node[propName] === propValue : Boolean(node[propName]); +} diff --git a/packages/kbn-openapi-bundler/src/utils/insert_by_json_pointer.ts b/packages/kbn-openapi-bundler/src/utils/insert_by_json_pointer.ts new file mode 100644 index 0000000000000..8538102305edc --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/insert_by_json_pointer.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. + */ + +/** + * Inserts `data` into the location specified by pointer in the `document`. + * + * @param pointer [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) + * @param data An object to insert + * @param document A document to insert to + */ +export function insertRefByPointer( + pointer: string, + data: unknown, + document: Record +): void { + const segments = pointer.split('/').slice(2); + let target = document; + + while (segments.length > 0) { + const segment = segments.shift() as string; + + if (!target[segment]) { + target[segment] = {}; + } + + target = target[segment] as Record; + } + + Object.assign(target, data); +} diff --git a/packages/kbn-openapi-bundler/src/utils/is_plain_object_type.ts b/packages/kbn-openapi-bundler/src/utils/is_plain_object_type.ts new file mode 100644 index 0000000000000..5807bae63f281 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/is_plain_object_type.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { isPlainObject } from 'lodash'; + +export function isPlainObjectType(maybeObj: unknown): maybeObj is Record { + return isPlainObject(maybeObj); +} diff --git a/packages/kbn-openapi-bundler/src/utils/parse_ref.ts b/packages/kbn-openapi-bundler/src/utils/parse_ref.ts new file mode 100644 index 0000000000000..fd23bf0d9277b --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/parse_ref.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface ParsedRef { + path: string; + pointer: string; +} + +/** + * Parses [JSON Reference](https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03) + * + * @param ref JSON Reference + * @returns file path and JSON pointer + */ +export function parseRef(ref: string): ParsedRef { + const [filePath, pointer] = ref.split('#'); + + if (!pointer) { + throw new Error(`Unable to parse $ref "${ref}"`); + } + + return { + path: filePath, + pointer, + }; +} diff --git a/packages/kbn-openapi-bundler/src/utils/read_yaml_document.ts b/packages/kbn-openapi-bundler/src/utils/read_yaml_document.ts new file mode 100644 index 0000000000000..c8cbae710c1ba --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/read_yaml_document.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 fs from 'fs/promises'; +import { load } from 'js-yaml'; + +export async function readYamlDocument(filePath: string): Promise> { + // Typing load's result to Record is optimistic as we can't be sure + // there is object inside a yaml file. We don't have this validation layer so far + // but using JSON Schemas here should mitigate this problem. + return load(await fs.readFile(filePath, { encoding: 'utf8' })); +} diff --git a/packages/kbn-openapi-bundler/src/utils/remove_files_by_glob.ts b/packages/kbn-openapi-bundler/src/utils/remove_files_by_glob.ts new file mode 100644 index 0000000000000..db8ed245914ab --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/remove_files_by_glob.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import fs from 'fs/promises'; +import globby from 'globby'; +import { resolve } from 'path'; + +/** + * Removes any files matching glob pattern from the target directory + * + * @param path target directory + * @param globPattern files pattern to remove + */ +export async function removeFilesByGlob(path: string, globPattern: string): Promise { + const filesToRemove = await globby([resolve(path, globPattern)]); + + await Promise.all(filesToRemove.map((fileName) => fs.unlink(fileName))); +} diff --git a/packages/kbn-openapi-bundler/src/utils/to_absolute_path.ts b/packages/kbn-openapi-bundler/src/utils/to_absolute_path.ts new file mode 100644 index 0000000000000..db63feae1295c --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/to_absolute_path.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { join, isAbsolute } from 'path'; + +/** + * Transforms a path to absolute path. If an absolute path passed to the function it's returned without + * changes. Base path is current working directory by default. + * + * @param maybeAbsolutePath a path to be transformed into absolute path + * @param baseDirPath a path from root to the folder maybeAbsolutePath is relative to + * @returns absolute path + */ +export function toAbsolutePath(maybeAbsolutePath: string, baseDirPath: string): string { + if (isAbsolute(maybeAbsolutePath)) { + return maybeAbsolutePath; + } + + return join(baseDirPath, maybeAbsolutePath); +} diff --git a/packages/kbn-openapi-bundler/src/utils/write_yaml_document.ts b/packages/kbn-openapi-bundler/src/utils/write_yaml_document.ts new file mode 100644 index 0000000000000..bdcd783e1a214 --- /dev/null +++ b/packages/kbn-openapi-bundler/src/utils/write_yaml_document.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 fs from 'fs/promises'; +import { dump } from 'js-yaml'; +import { dirname } from 'path'; + +export async function writeYamlDocument(filePath: string, document: unknown): Promise { + try { + const yaml = dump(document, { noRefs: true }); + + await fs.mkdir(dirname(filePath), { recursive: true }); + await fs.writeFile(filePath, yaml); + } catch (e) { + throw new Error(`Unable to write bundled yaml: ${e.message}`, { cause: e }); + } +} diff --git a/packages/kbn-subscription-tracking/tsconfig.json b/packages/kbn-openapi-bundler/tsconfig.json similarity index 51% rename from packages/kbn-subscription-tracking/tsconfig.json rename to packages/kbn-openapi-bundler/tsconfig.json index 677e9db998bb7..79d0eba0851b6 100644 --- a/packages/kbn-subscription-tracking/tsconfig.json +++ b/packages/kbn-openapi-bundler/tsconfig.json @@ -1,10 +1,10 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "target/types", - "types": ["jest", "node", "react"] + "types": ["jest", "node"] }, - "include": ["**/*.ts", "**/*.tsx"], "exclude": ["target/**/*"], - "kbn_references": ["@kbn/analytics-client"] + "extends": "../../tsconfig.base.json", + "include": ["**/*.ts"], + "kbn_references": ["@kbn/tooling-log"] } diff --git a/packages/kbn-openapi-generator/src/template_service/register_helpers.ts b/packages/kbn-openapi-generator/src/template_service/register_helpers.ts index 72c6b50d12666..3b4b5fa9202fd 100644 --- a/packages/kbn-openapi-generator/src/template_service/register_helpers.ts +++ b/packages/kbn-openapi-generator/src/template_service/register_helpers.ts @@ -47,4 +47,7 @@ export function registerHelpers(handlebarsInstance: typeof Handlebars) { handlebarsInstance.registerHelper('isUnknown', (val: object) => { return !('type' in val || '$ref' in val || 'anyOf' in val || 'oneOf' in val || 'allOf' in val); }); + handlebarsInstance.registerHelper('startsWithSpecialChar', (val: string) => { + return /^[^a-zA-Z0-9]/.test(val); + }); } diff --git a/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars b/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars index 9a5312cf88f53..29d4453da4d7b 100644 --- a/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars +++ b/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars @@ -12,7 +12,6 @@ {{~#if (defined default)}}.default({{{toJSON default}}}){{/if~}} {{~#if (eq x-modify "partial")}}.partial(){{/if~}} {{~#if (eq x-modify "required")}}.required(){{/if~}} - {{~#if (eq x-modify "requiredOptional")}}.transform(requiredOptional){{/if~}} {{~/if~}} {{~#if allOf~}} @@ -20,26 +19,34 @@ {{~#if @first~}} {{> zod_schema_item }} {{~else~}} - .and({{> zod_schema_item }}) + .merge({{> zod_schema_item }}) {{~/if~}} {{~/each~}} {{~/if~}} {{~#if anyOf~}} - z.union([ - {{~#each anyOf~}} + {{#if discriminator}} + z.discriminatedUnion('{{discriminator.propertyName}}', [ + {{else}} + z.union([ + {{/if}} + {{~#each anyOf~}} {{~> zod_schema_item ~}}, - {{~/each~}} + {{~/each~}} ]) {{~#if nullable}}.nullable(){{/if~}} {{~#if (eq requiredBool false)}}.optional(){{/if~}} {{~/if~}} {{~#if oneOf~}} - z.union([ - {{~#each oneOf~}} + {{#if discriminator}} + z.discriminatedUnion('{{discriminator.propertyName}}', [ + {{else}} + z.union([ + {{/if}} + {{~#each oneOf~}} {{~> zod_schema_item ~}}, - {{~/each~}} + {{~/each~}} ]) {{~#if nullable}}.nullable(){{/if~}} {{~#if (eq requiredBool false)}}.optional(){{/if~}} @@ -80,7 +87,11 @@ z.unknown() * {{{description}}} */ {{/if}} - {{@key}}: {{> zod_schema_item requiredBool=(includes ../required @key)}}, + {{#if (startsWithSpecialChar @key)}} + '{{@key}}': {{> zod_schema_item requiredBool=(includes ../required @key)}}, + {{else}} + {{@key}}: {{> zod_schema_item requiredBool=(includes ../required @key)}}, + {{/if}} {{/each}} }) {{~#if (eq additionalProperties false)}}.strict(){{/if~}} @@ -93,7 +104,6 @@ z.unknown() {{~/if~}} {{~#if (eq x-modify "partial")}}.partial(){{/if~}} {{~#if (eq x-modify "required")}}.required(){{/if~}} - {{~#if (eq x-modify "requiredOptional")}}.transform(requiredOptional){{/if~}} {{~/inline~}} {{~#*inline "type_string"~}} diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e79e6b4fa8be9..9db1810fd2b7f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -95,7 +95,7 @@ pageLoadAssetSize: management: 46112 maps: 90000 mapsEms: 26072 - metricsDataAccess: 60000 + metricsDataAccess: 73287 ml: 82187 monitoring: 80000 navigation: 37269 diff --git a/packages/kbn-panel-loader/README.md b/packages/kbn-panel-loader/README.md new file mode 100644 index 0000000000000..5b14fa96c6c40 --- /dev/null +++ b/packages/kbn-panel-loader/README.md @@ -0,0 +1,3 @@ +# @kbn/panel-loader + +Contains a generic loader which should be used to indicate that a chart is loading diff --git a/src/plugins/embeddable/public/embeddable_panel/embeddable_loading_indicator.tsx b/packages/kbn-panel-loader/index.tsx similarity index 78% rename from src/plugins/embeddable/public/embeddable_panel/embeddable_loading_indicator.tsx rename to packages/kbn-panel-loader/index.tsx index e0a4ca5cf5fe7..ad4e8751910f3 100644 --- a/src/plugins/embeddable/public/embeddable_panel/embeddable_loading_indicator.tsx +++ b/packages/kbn-panel-loader/index.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { EuiLoadingChart, EuiPanel } from '@elastic/eui'; -export const EmbeddableLoadingIndicator = ({ showShadow }: { showShadow?: boolean }) => { +export const PanelLoader = (props: { showShadow?: boolean; dataTestSubj?: string }) => { return ( diff --git a/packages/kbn-panel-loader/jest.config.js b/packages/kbn-panel-loader/jest.config.js new file mode 100644 index 0000000000000..e8cfab95a0732 --- /dev/null +++ b/packages/kbn-panel-loader/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-panel-loader'], +}; diff --git a/packages/kbn-panel-loader/kibana.jsonc b/packages/kbn-panel-loader/kibana.jsonc new file mode 100644 index 0000000000000..5fc518a8983ca --- /dev/null +++ b/packages/kbn-panel-loader/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-browser", + "id": "@kbn/panel-loader", + "owner": "@elastic/kibana-presentation" +} diff --git a/packages/kbn-panel-loader/package.json b/packages/kbn-panel-loader/package.json new file mode 100644 index 0000000000000..94394420475b1 --- /dev/null +++ b/packages/kbn-panel-loader/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/panel-loader", + "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-panel-loader/tsconfig.json b/packages/kbn-panel-loader/tsconfig.json new file mode 100644 index 0000000000000..f885e788791b7 --- /dev/null +++ b/packages/kbn-panel-loader/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [] +} diff --git a/packages/kbn-profiling-utils/common/functions.ts b/packages/kbn-profiling-utils/common/functions.ts index 47bf0a3100077..50cbd40a6d191 100644 --- a/packages/kbn-profiling-utils/common/functions.ts +++ b/packages/kbn-profiling-utils/common/functions.ts @@ -207,3 +207,21 @@ export const topNFunctionSortFieldRt = t.union([ t.literal(TopNFunctionSortField.AnnualizedCo2), t.literal(TopNFunctionSortField.AnnualizedDollarCost), ]); + +export enum TopNComparisonFunctionSortField { + ComparisonRank = 'comparison_rank', + ComparisonFrame = 'comparison_frame', + ComparisonSamples = 'comparison_samples', + ComparisonSelfCPU = 'comparison_selfCPU', + ComparisonTotalCPU = 'comparison_totalCPU', + ComparisonDiff = 'comparison_diff', +} + +export const topNComparisonFunctionSortFieldRt = t.union([ + t.literal(TopNComparisonFunctionSortField.ComparisonRank), + t.literal(TopNComparisonFunctionSortField.ComparisonFrame), + t.literal(TopNComparisonFunctionSortField.ComparisonSamples), + t.literal(TopNComparisonFunctionSortField.ComparisonSelfCPU), + t.literal(TopNComparisonFunctionSortField.ComparisonTotalCPU), + t.literal(TopNComparisonFunctionSortField.ComparisonDiff), +]); diff --git a/packages/kbn-profiling-utils/index.ts b/packages/kbn-profiling-utils/index.ts index 38ab8d11be237..54376e29b6d10 100644 --- a/packages/kbn-profiling-utils/index.ts +++ b/packages/kbn-profiling-utils/index.ts @@ -30,6 +30,8 @@ export { createTopNFunctions, TopNFunctionSortField, topNFunctionSortFieldRt, + TopNComparisonFunctionSortField, + topNComparisonFunctionSortFieldRt, } from './common/functions'; export type { CalleeTree } from './common/callee'; diff --git a/packages/kbn-search-api-panels/components/ingest_data.tsx b/packages/kbn-search-api-panels/components/ingest_data.tsx index 0b0e11b13618d..f8ba59d29bf30 100644 --- a/packages/kbn-search-api-panels/components/ingest_data.tsx +++ b/packages/kbn-search-api-panels/components/ingest_data.tsx @@ -6,25 +6,22 @@ * Side Public License, v 1. */ -import React, { useState } from 'react'; +import React from 'react'; -import { EuiCheckableCard, EuiFormFieldset, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ApplicationStart } from '@kbn/core-application-browser'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { CodeBox } from './code_box'; import { LanguageDefinition } from '../types'; import { OverviewPanel } from './overview_panel'; -import { IntegrationsPanel } from './integrations_panel'; - +import { IngestionsPanel } from './ingestions_panel'; interface IngestDataProps { codeSnippet: string; selectedLanguage: LanguageDefinition; setSelectedLanguage: (language: LanguageDefinition) => void; docLinks: { beats: string; - connectors: string; - integrations: string; logstash: string; }; assetBasePath: string; @@ -32,6 +29,7 @@ interface IngestDataProps { sharePlugin: SharePluginStart; languages: LanguageDefinition[]; consoleRequest?: string; + additionalIngestionPanel?: React.ReactNode; } export const IngestData: React.FC = ({ @@ -44,115 +42,45 @@ export const IngestData: React.FC = ({ sharePlugin, languages, consoleRequest, + additionalIngestionPanel, }) => { - const [selectedIngestMethod, setSelectedIngestMethod] = useState< - 'ingestViaApi' | 'ingestViaIntegration' - >('ingestViaApi'); return ( - ) : ( - - ) + } - links={[ - ...(selectedLanguage.apiReference - ? [ - { - href: selectedLanguage.apiReference, - label: i18n.translate('searchApiPanels.welcomeBanner.ingestData.clientDocLink', { - defaultMessage: '{languageName} API reference', - values: { languageName: selectedLanguage.name }, - }), - }, - ] - : []), - { - href: docLinks.integrations, - label: i18n.translate('searchApiPanels.welcomeBanner.ingestData.integrationsLink', { - defaultMessage: 'About Integrations', - }), - }, - ]} + links={[]} title={i18n.translate('searchApiPanels.welcomeBanner.ingestData.title', { defaultMessage: 'Ingest data', })} > - - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.ingestApiLabel', { - defaultMessage: 'Ingest via API', - })} -

- - } - value="ingestViaApi" - checked={selectedIngestMethod === 'ingestViaApi'} - onChange={() => setSelectedIngestMethod('ingestViaApi')} - > - - {i18n.translate('searchApiPanels.welcomeBanner.ingestData.ingestApiDescription', { - defaultMessage: - 'The most flexible way to index data, enabling full control over your customization and optimization options.', - })} - -
- - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.ingestIntegrationLabel', { - defaultMessage: 'Ingest via integration', - })} -

- - } - value="ingestViaIntegration" - checked={selectedIngestMethod === 'ingestViaIntegration'} - onChange={() => setSelectedIngestMethod('ingestViaIntegration')} - > - - {i18n.translate( - 'searchApiPanels.welcomeBanner.ingestData.ingestIntegrationDescription', - { - defaultMessage: - 'Specialized ingestion tools optimized for transforming data and shipping it to Elasticsearch.', - } - )} - -
-
+ +

+ {i18n.translate('searchApiPanels.welcomeBanner.ingestData.alternativeOptions', { + defaultMessage: 'Alternative ingestion options', + })} +

+
+ + +
); }; diff --git a/packages/kbn-search-api-panels/components/ingestions_panel.tsx b/packages/kbn-search-api-panels/components/ingestions_panel.tsx new file mode 100644 index 0000000000000..ef9a0ef95ad09 --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingestions_panel.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiText, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { GithubLink } from './github_link'; + +interface IngestionPanelProps { + additionalIngestionPanel?: React.ReactNode; + docLinks: { beats: string; logstash: string }; + assetBasePath: string; +} + +export const IngestionsPanel: React.FC = ({ + additionalIngestionPanel, + docLinks, + assetBasePath, +}) => { + const panels = [ + { + description: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.logstashDescription', + { + defaultMessage: + 'General-purpose data processing pipeline for Elasticsearch. Use Logstash to extract and transform data from a variety of inputs and outputs.', + } + ), + title: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.logstashTitle', + { + defaultMessage: 'Logstash', + } + ), + links: [ + { + href: docLinks.logstash, + label: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.logstashDocumentationLabel', + { + defaultMessage: 'Documentation', + } + ), + }, + { + href: 'https://github.com/elastic/logstash', + isGithubLink: true, + label: 'logstash', + }, + ], + }, + { + description: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.beatsDescription', + { + defaultMessage: + 'Lightweight, single-purpose data shippers for Elasticsearch. Use Beats to send operational data from your servers.', + } + ), + title: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.beatsTitle', + { + defaultMessage: 'Beats', + } + ), + links: [ + { + href: docLinks.beats, + label: i18n.translate( + 'searchApiPanels.welcomeBanner.ingestData.alternativeOptions.beatsDocumentationLabel', + { + defaultMessage: 'Documentation', + } + ), + }, + { + href: 'https://github.com/elastic/beats', + isGithubLink: true, + label: 'beats', + }, + ], + }, + ]; + return ( + <> + {additionalIngestionPanel} + {panels.map(({ title, description, links }, panelIndex) => ( + + + +
{title}
+
+ + +

{description}

+
+
+ {links && links.length > 0 && ( + <> + + {links.map(({ label, href, isGithubLink }, linksIndex) => ( + + {isGithubLink ? ( + + ) : ( + + {label} + + )} + + ))} + + + + )} +
+ ))} + + ); +}; diff --git a/packages/kbn-search-api-panels/components/integrations_panel.tsx b/packages/kbn-search-api-panels/components/integrations_panel.tsx deleted file mode 100644 index 3f6005f26cebd..0000000000000 --- a/packages/kbn-search-api-panels/components/integrations_panel.tsx +++ /dev/null @@ -1,168 +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 { - EuiThemeProvider, - EuiPanel, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiTitle, - EuiSpacer, - EuiText, - EuiLink, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { LEARN_MORE_LABEL } from '../constants'; -import { GithubLink } from './github_link'; - -export interface IntegrationsPanelProps { - docLinks: { beats: string; connectors: string; logstash: string }; - assetBasePath: string; -} - -export const IntegrationsPanel: React.FC = ({ - docLinks, - assetBasePath, -}) => { - return ( - - - - - - - - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.logstashTitle', { - defaultMessage: 'Logstash', - })} -

-
- - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.logstashDescription', { - defaultMessage: - 'Add data to your data stream or index to make it searchable. Choose an ingestion method that fits your application and workflow.', - })} -

-
- - - - - - {LEARN_MORE_LABEL} - - - - - - - -
-
- - - - - - - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.beatsTitle', { - defaultMessage: 'Beats', - })} -

-
- - - {i18n.translate('searchApiPanels.welcomeBanner.ingestData.beatsDescription', { - defaultMessage: - 'Lightweight, single-purpose data shippers for Elasticsearch. Use Beats to send operational data from your servers.', - })} - - - - - - - {LEARN_MORE_LABEL} - - - - - - - -
-
- - - - - - - -

- {i18n.translate('searchApiPanels.welcomeBanner.ingestData.connectorsTitle', { - defaultMessage: 'Connector clients', - })} -

-
- - - {i18n.translate('searchApiPanels.welcomeBanner.ingestData.connectorsDescription', { - defaultMessage: - 'Specialized integrations for syncing data from third-party sources to Elasticsearch. Use Elastic connectors to sync content from a range of databases and object stores.', - })} - - - - - - - {LEARN_MORE_LABEL} - - - - - - - -
-
-
-
- ); -}; diff --git a/packages/kbn-search-api-panels/index.tsx b/packages/kbn-search-api-panels/index.tsx index 059aaf184ebf5..513cc0fda807c 100644 --- a/packages/kbn-search-api-panels/index.tsx +++ b/packages/kbn-search-api-panels/index.tsx @@ -14,7 +14,7 @@ import { AuthenticatedUser } from '@kbn/security-plugin/common'; export * from './components/code_box'; export * from './components/github_link'; export * from './components/ingest_data'; -export * from './components/integrations_panel'; +export * from './components/ingestions_panel'; export * from './components/language_client_panel'; export * from './components/overview_panel'; export * from './components/select_client'; @@ -54,18 +54,18 @@ export const WelcomeBanner: React.FC = ({ {Boolean(user) && ( - +

{user ? i18n.translate('searchApiPanels.welcomeBanner.header.greeting.customTitle', { - defaultMessage: 'Hi {name}!', + defaultMessage: '👋 Hi {name}!', values: { name: user.full_name || user.username }, }) : i18n.translate('searchApiPanels.welcomeBanner.header.greeting.defaultTitle', { - defaultMessage: 'Hi!', + defaultMessage: '👋 Hi', })}

-
+
)} diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx index f0b5931e54436..9eab42f5cec73 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx @@ -14,7 +14,7 @@ import { AutocompleteFieldWildcardComponent } from '.'; import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete'; import { fields, getField } from '../fields/index.mock'; import { autocompleteStartMock } from '../autocomplete/index.mock'; -import { FILENAME_WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils'; +import { WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils'; jest.mock('../hooks/use_field_value_autocomplete'); jest.mock('../translations', () => ({ @@ -368,7 +368,7 @@ describe('AutocompleteFieldWildcardComponent', () => { placeholder="Placeholder text" selectedField={getField('file.path.text')} selectedValue="invalid path" - warning={FILENAME_WILDCARD_WARNING} + warning={WILDCARD_WARNING} /> ); @@ -384,7 +384,7 @@ describe('AutocompleteFieldWildcardComponent', () => { const helpText = wrapper .find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText') .at(0); - expect(helpText.text()).toEqual(FILENAME_WILDCARD_WARNING); + expect(helpText.text()).toEqual(WILDCARD_WARNING); expect(helpText.find('.euiToolTipAnchor')).toBeTruthy(); }); test('should show the warning helper text if the new value contains spaces when change', async () => { @@ -412,7 +412,7 @@ describe('AutocompleteFieldWildcardComponent', () => { placeholder="Placeholder text" selectedField={getField('file.path.text')} selectedValue="invalid path" - warning={FILENAME_WILDCARD_WARNING} + warning={WILDCARD_WARNING} /> ); diff --git a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx index 3595b96d7e426..954b14a8ea244 100644 --- a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx @@ -223,6 +223,7 @@ describe('operator', () => { { label: 'is one of' }, { label: 'is not one of' }, { label: 'matches' }, + { label: 'does not match' }, ]); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx b/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx index 2172390f41e85..9f09e3b26ea49 100644 --- a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx @@ -124,4 +124,52 @@ describe('group selector', () => { ); expect(getByTestId('group-selector-dropdown').title).toEqual('Rule name, Host name'); }); + describe('when maxGroupingLevels is 1', () => { + it('Presents single option selector label when dropdown is clicked', () => { + const { getByTestId } = render( + + ); + fireEvent.click(getByTestId('group-selector-dropdown')); + expect(getByTestId('contextMenuPanelTitle').textContent).toMatch(/select grouping/i); + }); + it('Does not disable any options when maxGroupingLevels is 1 and one option is selected', () => { + const groupSelected = ['kibana.alert.rule.name']; + + const { getByTestId } = render( + + ); + + fireEvent.click(getByTestId('group-selector-dropdown')); + + [...testProps.options, { key: 'custom', label: 'Custom field' }].forEach((o) => { + expect(getByTestId(`panel-${o.key}`)).not.toHaveAttribute('disabled'); + }); + }); + }); + describe('when maxGroupingLevels is greater than 1', () => { + it('Presents select up to "X" groupings when dropdown is clicked', () => { + const { getByTestId } = render( + + ); + fireEvent.click(getByTestId('group-selector-dropdown')); + expect(getByTestId('contextMenuPanelTitle').textContent).toMatch(/select up to 3 groupings/i); + }); + it('Disables non-selected options when maxGroupingLevels is greater than 1 and the selects items reaches the maxGroupingLevels', () => { + const groupSelected = ['kibana.alert.rule.name', 'user.name']; + + const { getByTestId } = render( + + ); + + fireEvent.click(getByTestId('group-selector-dropdown')); + + [...testProps.options, { key: 'custom', label: 'Custom field' }].forEach((o) => { + if (groupSelected.includes(o.key) || o.key === 'none') { + expect(getByTestId(`panel-${o.key}`)).not.toHaveAttribute('disabled'); + } else { + expect(getByTestId(`panel-${o.key}`)).toHaveAttribute('disabled'); + } + }); + }); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx b/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx index 3fa82f7f8af20..ffec5db362066 100644 --- a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx @@ -43,11 +43,21 @@ const GroupSelectorComponent = ({ [groupsSelected] ); - const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => [ + const panels: EuiContextMenuPanelDescriptor[] = useMemo(() => { + const isOptionDisabled = (key?: string) => { + // Do not disable when maxGroupingLevels is 1 to allow toggling between groups + if (maxGroupingLevels === 1) { + return false; + } + // Disable all non selected options when the maxGroupingLevels is reached + return groupsSelected.length === maxGroupingLevels && (key ? !isGroupSelected(key) : true); + }; + + return [ { id: 'firstPanel', - title: i18n.SELECT_FIELD(maxGroupingLevels), + title: + maxGroupingLevels === 1 ? i18n.SELECT_SINGLE_FIELD : i18n.SELECT_FIELD(maxGroupingLevels), items: [ { 'data-test-subj': 'panel-none', @@ -57,7 +67,7 @@ const GroupSelectorComponent = ({ }, ...options.map((o) => ({ 'data-test-subj': `panel-${o.key}`, - disabled: groupsSelected.length === maxGroupingLevels && !isGroupSelected(o.key), + disabled: isOptionDisabled(o.key), name: o.label, onClick: () => onGroupChange(o.key), icon: isGroupSelected(o.key) ? 'check' : 'empty', @@ -66,7 +76,7 @@ const GroupSelectorComponent = ({ 'data-test-subj': `panel-custom`, name: i18n.CUSTOM_FIELD, icon: 'empty', - disabled: groupsSelected.length === maxGroupingLevels, + disabled: isOptionDisabled(), panel: 'customPanel', hasPanel: true, }, @@ -87,9 +97,8 @@ const GroupSelectorComponent = ({ /> ), }, - ], - [fields, groupsSelected.length, isGroupSelected, maxGroupingLevels, onGroupChange, options] - ); + ]; + }, [fields, groupsSelected.length, isGroupSelected, maxGroupingLevels, onGroupChange, options]); const selectedOptions = useMemo( () => options.filter((groupOption) => isGroupSelected(groupOption.key)), [isGroupSelected, options] diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx index 2b581ed774d5d..0cf16ae4c8217 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx @@ -136,4 +136,53 @@ describe('grouping container', () => { expect(renderChildComponent).toHaveBeenCalledWith(getNullGroupFilter('host.name')); }); + + it('Renders groupPanelRenderer when provided', () => { + const groupPanelRenderer = jest.fn(); + render( + + + + ); + + expect(groupPanelRenderer).toHaveBeenNthCalledWith( + 1, + 'host.name', + testProps.data.groupByFields.buckets[0], + undefined, + false + ); + + expect(groupPanelRenderer).toHaveBeenNthCalledWith( + 2, + 'host.name', + testProps.data.groupByFields.buckets[1], + undefined, + false + ); + + expect(groupPanelRenderer).toHaveBeenNthCalledWith( + 3, + 'host.name', + testProps.data.groupByFields.buckets[2], + 'The selected group by field, host.name, is missing a value for this group of events.', + false + ); + }); + it('Renders groupPanelRenderer when provided with isLoading attribute', () => { + const groupPanelRenderer = jest.fn(); + render( + + + + ); + + expect(groupPanelRenderer).toHaveBeenNthCalledWith( + 1, + 'host.name', + testProps.data.groupByFields.buckets[0], + undefined, + true + ); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index aab42a0804e4c..5ae1037d9edb3 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -128,7 +128,7 @@ const GroupingComponent = ({ groupBucket={groupBucket} groupPanelRenderer={ groupPanelRenderer && - groupPanelRenderer(selectedGroup, groupBucket, nullGroupMessage) + groupPanelRenderer(selectedGroup, groupBucket, nullGroupMessage, isLoading) } isLoading={isLoading} onToggleGroup={(isOpen) => { diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-securitysolution-grouping/src/components/translations.ts index c2e0f13e325a5..de5337db904da 100644 --- a/packages/kbn-securitysolution-grouping/src/components/translations.ts +++ b/packages/kbn-securitysolution-grouping/src/components/translations.ts @@ -32,6 +32,10 @@ export const SELECT_FIELD = (groupingLevelsCount: number) => defaultMessage: 'Select up to {groupingLevelsCount} groupings', }); +export const SELECT_SINGLE_FIELD = i18n.translate('grouping.groupBySingleField', { + defaultMessage: 'Select grouping', +}); + export const NONE = i18n.translate('grouping.noneGroupByOptionName', { defaultMessage: 'None', }); diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index 6987a09c083f8..43a9af13372f7 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -76,7 +76,8 @@ export type GroupStatsRenderer = ( export type GroupPanelRenderer = ( selectedGroup: string, fieldBucket: RawBucket, - nullGroupMessage?: string + nullGroupMessage?: string, + isLoading?: boolean ) => JSX.Element | undefined; export type OnGroupToggle = (params: { diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts index b208aef1e840b..69a284ecf7a59 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts @@ -19,11 +19,11 @@ type RunTimeMappings = | Record & { type: RuntimePrimitiveTypes }> | undefined; -interface BoolAgg { +export interface BoolAgg { bool: BoolQuery; } -interface RangeAgg { +export interface RangeAgg { range: { '@timestamp': { gte: string; lte: string } }; } diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx index 36fe9badac55d..e6221c677b9d5 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx @@ -157,6 +157,34 @@ describe('Group Selector Hooks', () => { }); }); + it('On group change when maxGroupingLevels is 1, remove previously selected group', () => { + const testGroup = { + [groupingId]: { + ...defaultGroup, + options: defaultGroupingOptions, + activeGroups: ['host.name'], + }, + }; + const { result } = renderHook((props) => useGetGroupSelector(props), { + initialProps: { + ...defaultArgs, + maxGroupingLevels: 1, + groupingState: { + groupById: testGroup, + }, + }, + }); + act(() => result.current.props.onGroupChange('user.name')); + + expect(dispatch).toHaveBeenCalledWith({ + payload: { + id: groupingId, + activeGroups: ['user.name'], + }, + type: ActionType.updateActiveGroups, + }); + }); + it('On group change, resets active page, sets active group, and leaves options alone', () => { const testGroup = { [groupingId]: { diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx index d50809c99fb3f..d3e518a48fe59 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx @@ -30,6 +30,7 @@ export interface UseGetGroupSelectorArgs { event: string | string[], count?: number | undefined ) => void; + title?: string; } interface UseGetGroupSelectorStateless @@ -84,6 +85,7 @@ export const useGetGroupSelector = ({ onGroupChange, onOptionsChange, tracker, + title, }: UseGetGroupSelectorArgs) => { const { activeGroups: selectedGroups, options } = groupByIdSelector({ groups: groupingState }, groupingId) ?? defaultGroup; @@ -110,20 +112,25 @@ export const useGetGroupSelector = ({ const onChange = useCallback( (groupSelection: string) => { - if (selectedGroups.find((selected) => selected === groupSelection)) { - const groups = selectedGroups.filter((selectedGroup) => selectedGroup !== groupSelection); - if (groups.length === 0) { - setSelectedGroups(['none']); - } else { - setSelectedGroups(groups); + // Simulate a toggle behavior when maxGroupingLevels is 1 + if (maxGroupingLevels === 1) { + setSelectedGroups([groupSelection]); + } else { + if (selectedGroups.find((selected) => selected === groupSelection)) { + const groups = selectedGroups.filter((selectedGroup) => selectedGroup !== groupSelection); + if (groups.length === 0) { + setSelectedGroups(['none']); + } else { + setSelectedGroups(groups); + } + return; } - return; - } - const newSelectedGroups = isNoneGroup([groupSelection]) - ? [groupSelection] - : [...selectedGroups.filter((selectedGroup) => selectedGroup !== 'none'), groupSelection]; - setSelectedGroups(newSelectedGroups); + const newSelectedGroups = isNoneGroup([groupSelection]) + ? [groupSelection] + : [...selectedGroups.filter((selectedGroup) => selectedGroup !== 'none'), groupSelection]; + setSelectedGroups(newSelectedGroups); + } // built-in telemetry: UI-counter tracker?.( @@ -133,7 +140,7 @@ export const useGetGroupSelector = ({ onGroupChange?.({ tableId: groupingId, groupByField: groupSelection }); }, - [groupingId, onGroupChange, selectedGroups, setSelectedGroups, tracker] + [groupingId, maxGroupingLevels, onGroupChange, selectedGroups, setSelectedGroups, tracker] ); useEffect(() => { @@ -184,6 +191,7 @@ export const useGetGroupSelector = ({ fields, maxGroupingLevels, options, + title, }} /> ); diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx index a744fc6c4ff7c..3ff2232acbd72 100644 --- a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx @@ -19,7 +19,7 @@ import { Grouping as GroupingComponent } from '../components/grouping'; /** Interface for grouping object where T is the `GroupingAggregation` * @interface GroupingArgs */ -interface Grouping { +export interface UseGrouping { getGrouping: (props: DynamicGroupingProps) => React.ReactElement; groupSelector: React.ReactElement; selectedGroups: string[]; @@ -72,6 +72,7 @@ interface GroupingArgs { event: string | string[], count?: number | undefined ) => void; + title?: string; } /** @@ -85,6 +86,7 @@ interface GroupingArgs { * @param onGroupChange callback executed when selected group is changed, used for tracking * @param onOptionsChange callback executed when grouping options are changed, used for consumer grouping selector * @param tracker telemetry handler + * @param title of the grouping selector component * @returns {@link Grouping} the grouping constructor { getGrouping, groupSelector, pagination, selectedGroups } */ export const useGrouping = ({ @@ -96,7 +98,8 @@ export const useGrouping = ({ onGroupChange, onOptionsChange, tracker, -}: GroupingArgs): Grouping => { + title, +}: GroupingArgs): UseGrouping => { const [groupingState, dispatch] = useReducer(groupsReducerWithStorage, initialState); const { activeGroups: selectedGroups } = useMemo( () => groupByIdSelector({ groups: groupingState }, groupingId) ?? defaultGroup, @@ -125,6 +128,7 @@ export const useGrouping = ({ onGroupChange, onOptionsChange, tracker, + title, }); const getGrouping = useCallback( diff --git a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts index f9caa6387e359..c236cacad6a2c 100644 --- a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts @@ -109,6 +109,7 @@ export const EVENT_FILTERS_OPERATORS: OperatorOption[] = [ isOneOfOperator, isNotOneOfOperator, matchesOperator, + doesNotMatchOperator, ]; /* diff --git a/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts b/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts index 5bb84816b1602..f877683caec10 100644 --- a/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts +++ b/packages/kbn-securitysolution-utils/src/path_validations/index.test.ts @@ -11,11 +11,43 @@ import { hasSimpleExecutableName, OperatingSystem, ConditionEntryField, + validatePotentialWildcardInput, validateFilePathInput, - FILENAME_WILDCARD_WARNING, + validateWildcardInput, + WILDCARD_WARNING, FILEPATH_WARNING, } from '.'; +describe('validatePotentialWildcardInput', () => { + it('warns on wildcard when field is file.path.text', () => { + expect( + validatePotentialWildcardInput({ + field: 'file.path.text', + os: OperatingSystem.WINDOWS, + value: 'c:\\path*.exe', + }) + ).toEqual(WILDCARD_WARNING); + }); + it('warns on wildcard when field is not file.path.text', () => { + expect( + validatePotentialWildcardInput({ + field: 'event.category', + os: OperatingSystem.WINDOWS, + value: 'some*value', + }) + ).toEqual(WILDCARD_WARNING); + }); +}); + +describe('validateWildcardInput', () => { + it('warns on wildcard for fields that are not file paths', () => { + expect(validateWildcardInput('*')).toEqual(WILDCARD_WARNING); + }); + it('does not warn if no wildcard', () => { + expect(validateWildcardInput('non-wildcard')).toEqual(undefined); + }); +}); + describe('validateFilePathInput', () => { describe('windows', () => { const os = OperatingSystem.WINDOWS; @@ -36,15 +68,13 @@ describe('validateFilePathInput', () => { }); it('warns on wildcard in file name at the end of the path', () => { - expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual( - FILENAME_WILDCARD_WARNING - ); + expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual(WILDCARD_WARNING); expect( validateFilePathInput({ os, value: 'C:\\Windows\\*\\FILENAME.EXE-*.gz', }) - ).toEqual(FILENAME_WILDCARD_WARNING); + ).toEqual(WILDCARD_WARNING); }); it('warns on unix paths or non-windows paths', () => { @@ -65,20 +95,23 @@ describe('validateFilePathInput', () => { : OperatingSystem.LINUX; it('does not warn on valid filenames', () => { - expect(validateFilePathInput({ os, value: '/opt/*/FILENAME.EXE-1231205124.gz' })).not.toEqual( - FILENAME_WILDCARD_WARNING - ); + expect( + validateFilePathInput({ + os, + value: '/opt/*/FILENAME.EXE-1231205124.gz', + }) + ).not.toEqual(WILDCARD_WARNING); expect( validateFilePathInput({ os, value: "/opt/*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt", }) - ).not.toEqual(FILENAME_WILDCARD_WARNING); + ).not.toEqual(WILDCARD_WARNING); }); it('warns on wildcard in file name at the end of the path', () => { - expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(FILENAME_WILDCARD_WARNING); + expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(WILDCARD_WARNING); expect(validateFilePathInput({ os, value: '/opt/FILENAME.EXE-*.gz' })).toEqual( - FILENAME_WILDCARD_WARNING + WILDCARD_WARNING ); }); diff --git a/packages/kbn-securitysolution-utils/src/path_validations/index.ts b/packages/kbn-securitysolution-utils/src/path_validations/index.ts index ac7c17426e723..b2ae2d9fbceb7 100644 --- a/packages/kbn-securitysolution-utils/src/path_validations/index.ts +++ b/packages/kbn-securitysolution-utils/src/path_validations/index.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; -export const FILENAME_WILDCARD_WARNING = i18n.translate('utils.filename.wildcardWarning', { - defaultMessage: `Using wildcards in file paths can impact Endpoint performance`, +export const WILDCARD_WARNING = i18n.translate('utils.wildcardWarning', { + defaultMessage: `Using wildcards can impact Endpoint performance`, }); export const FILEPATH_WARNING = i18n.translate('utils.filename.pathWarning', { @@ -52,39 +52,60 @@ export enum OperatingSystem { export type EntryTypes = 'match' | 'wildcard' | 'match_any'; export type TrustedAppEntryTypes = Extract; -export const validateFilePathInput = ({ +export const validatePotentialWildcardInput = ({ + field = '', os, value = '', }: { + field?: string; os: OperatingSystem; value?: string; }): string | undefined => { const textInput = value.trim(); + if (field === 'file.path.text') { + return validateFilePathInput({ os, value: textInput }); + } + return validateWildcardInput(textInput); +}; + +export const validateFilePathInput = ({ + os, + value, +}: { + os: OperatingSystem; + value: string; +}): string | undefined => { const isValidFilePath = isPathValid({ os, field: 'file.path.text', type: 'wildcard', - value: textInput, + value, }); const hasSimpleFileName = hasSimpleExecutableName({ os, type: 'wildcard', - value: textInput, + value, }); - if (!textInput.length) { + if (!value.length) { return FILEPATH_WARNING; } if (isValidFilePath) { if (hasSimpleFileName !== undefined && !hasSimpleFileName) { - return FILENAME_WILDCARD_WARNING; + return WILDCARD_WARNING; } } else { return FILEPATH_WARNING; } }; +export const validateWildcardInput = (value?: string): string | undefined => { + if (/[*?]/.test(value ?? '')) { + return WILDCARD_WARNING; + } +}; + export const hasSimpleExecutableName = ({ os, type, diff --git a/packages/kbn-server-http-tools/index.ts b/packages/kbn-server-http-tools/index.ts index 3e172297bf6c7..a572cc6ab0832 100644 --- a/packages/kbn-server-http-tools/index.ts +++ b/packages/kbn-server-http-tools/index.ts @@ -10,6 +10,7 @@ export type { IHttpConfig, ISslConfig, ICorsConfig } from './src/types'; export { createServer } from './src/create_server'; export { defaultValidationErrorHandler } from './src/default_validation_error_handler'; export { getListenerOptions } from './src/get_listener_options'; -export { getServerOptions } from './src/get_server_options'; +export { getServerOptions, getServerTLSOptions } from './src/get_server_options'; export { getRequestId } from './src/get_request_id'; +export { setTlsConfig } from './src/set_tls_config'; export { sslSchema, SslConfig } from './src/ssl'; diff --git a/packages/kbn-server-http-tools/src/get_server_options.ts b/packages/kbn-server-http-tools/src/get_server_options.ts index ade90a0e0d3f5..5cac081fdd148 100644 --- a/packages/kbn-server-http-tools/src/get_server_options.ts +++ b/packages/kbn-server-http-tools/src/get_server_options.ts @@ -9,7 +9,7 @@ import { RouteOptionsCors, ServerOptions } from '@hapi/hapi'; import { ServerOptions as TLSOptions } from 'https'; import { defaultValidationErrorHandler } from './default_validation_error_handler'; -import { IHttpConfig } from './types'; +import { IHttpConfig, ISslConfig } from './types'; const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf']; @@ -50,26 +50,31 @@ export function getServerOptions(config: IHttpConfig, { configureTLS = true } = }, }; - if (configureTLS && config.ssl.enabled) { - const ssl = config.ssl; - - // TODO: Hapi types have a typo in `tls` property type definition: `https.RequestOptions` is used instead of - // `https.ServerOptions`, and `honorCipherOrder` isn't presented in `https.RequestOptions`. - const tlsOptions: TLSOptions = { - ca: ssl.certificateAuthorities, - cert: ssl.certificate, - ciphers: config.ssl.cipherSuites?.join(':'), - // We use the server's cipher order rather than the client's to prevent the BEAST attack. - honorCipherOrder: true, - key: ssl.key, - passphrase: ssl.keyPassphrase, - secureOptions: ssl.getSecureOptions ? ssl.getSecureOptions() : undefined, - requestCert: ssl.requestCert, - rejectUnauthorized: ssl.rejectUnauthorized, - }; - - options.tls = tlsOptions; + if (configureTLS) { + options.tls = getServerTLSOptions(config.ssl); } return options; } + +/** + * Converts Kibana `SslConfig` into `TLSOptions` that are accepted by the Hapi server, + * and by https.Server.setSecureContext() + */ +export function getServerTLSOptions(ssl: ISslConfig): TLSOptions | undefined { + if (!ssl.enabled) { + return undefined; + } + return { + ca: ssl.certificateAuthorities, + cert: ssl.certificate, + ciphers: ssl.cipherSuites?.join(':'), + // We use the server's cipher order rather than the client's to prevent the BEAST attack. + honorCipherOrder: true, + key: ssl.key, + passphrase: ssl.keyPassphrase, + secureOptions: ssl.getSecureOptions ? ssl.getSecureOptions() : undefined, + requestCert: ssl.requestCert, + rejectUnauthorized: ssl.rejectUnauthorized, + }; +} diff --git a/packages/kbn-server-http-tools/src/set_tls_config.test.mocks.ts b/packages/kbn-server-http-tools/src/set_tls_config.test.mocks.ts new file mode 100644 index 0000000000000..4b93301b334e5 --- /dev/null +++ b/packages/kbn-server-http-tools/src/set_tls_config.test.mocks.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getServerTLSOptionsMock = jest.fn(); + +jest.doMock('./get_server_options', () => { + const actual = jest.requireActual('./get_server_options'); + return { + ...actual, + getServerTLSOptions: getServerTLSOptionsMock, + }; +}); diff --git a/packages/kbn-server-http-tools/src/set_tls_config.test.ts b/packages/kbn-server-http-tools/src/set_tls_config.test.ts new file mode 100644 index 0000000000000..ea3e61c139448 --- /dev/null +++ b/packages/kbn-server-http-tools/src/set_tls_config.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright 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 { getServerTLSOptionsMock } from './set_tls_config.test.mocks'; +import { Server } from '@hapi/hapi'; +import type { ISslConfig } from './types'; +import { setTlsConfig } from './set_tls_config'; + +describe('setTlsConfig', () => { + beforeEach(() => { + getServerTLSOptionsMock.mockReset(); + getServerTLSOptionsMock.mockReturnValue({}); + }); + + it('throws when called for a non-TLS server', () => { + const server = new Server({}); + const config: ISslConfig = { enabled: true }; + expect(() => setTlsConfig(server, config)).toThrowErrorMatchingInlineSnapshot( + `"tried to set TLS config on a non-TLS http server"` + ); + }); + + it('calls `getServerTLSOptions` with the correct parameters', () => { + const server = new Server({}); + // easiest way to shim a tls.Server + (server.listener as any).setSecureContext = jest.fn(); + const config: ISslConfig = { enabled: true }; + + setTlsConfig(server, config); + + expect(getServerTLSOptionsMock).toHaveBeenCalledTimes(1); + expect(getServerTLSOptionsMock).toHaveBeenCalledWith(config); + }); + + it('throws when called for a disabled SSL config', () => { + const server = new Server({}); + // easiest way to shim a tls.Server + (server.listener as any).setSecureContext = jest.fn(); + const config: ISslConfig = { enabled: false }; + + getServerTLSOptionsMock.mockReturnValue(undefined); + + expect(() => setTlsConfig(server, config)).toThrowErrorMatchingInlineSnapshot( + `"tried to apply a disabled SSL config"` + ); + }); + + it('calls `setSecureContext` on the underlying server', () => { + const server = new Server({}); + // easiest way to shim a tls.Server + const setSecureContextMock = jest.fn(); + (server.listener as any).setSecureContext = setSecureContextMock; + const config: ISslConfig = { enabled: true }; + + const tlsConfig = { someTlsConfig: true }; + getServerTLSOptionsMock.mockReturnValue(tlsConfig); + + setTlsConfig(server, config); + + expect(setSecureContextMock).toHaveBeenCalledTimes(1); + expect(setSecureContextMock).toHaveBeenCalledWith(tlsConfig); + }); +}); diff --git a/packages/kbn-server-http-tools/src/set_tls_config.ts b/packages/kbn-server-http-tools/src/set_tls_config.ts new file mode 100644 index 0000000000000..1f2e1d70fa126 --- /dev/null +++ b/packages/kbn-server-http-tools/src/set_tls_config.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Server as HapiServer } from '@hapi/hapi'; +import type { Server as HttpServer } from 'http'; +import type { Server as TlsServer } from 'https'; +import type { ISslConfig } from './types'; +import { getServerTLSOptions } from './get_server_options'; + +function isServerTLS(server: HttpServer): server is TlsServer { + return 'setSecureContext' in server; +} + +export const setTlsConfig = (hapiServer: HapiServer, sslConfig: ISslConfig) => { + const server = hapiServer.listener; + if (!isServerTLS(server)) { + throw new Error('tried to set TLS config on a non-TLS http server'); + } + const tlsOptions = getServerTLSOptions(sslConfig); + if (!tlsOptions) { + throw new Error('tried to apply a disabled SSL config'); + } + server.setSecureContext(tlsOptions); +}; diff --git a/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts index 112fcd8a449f7..c6f701ef67a1e 100644 --- a/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts +++ b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts @@ -164,6 +164,79 @@ describe('#SslConfig', () => { expect(configValue.certificate).toEqual('content-of-another-path'); }); }); + + describe('isEqualTo()', () => { + const createEnabledConfig = (obj: any) => + createConfig({ + enabled: true, + key: 'same-key', + certificate: 'same-cert', + ...obj, + }); + + it('compares `enabled`', () => { + const reference = createConfig({ enabled: true, key: 'same-key', certificate: 'same-cert' }); + const same = createConfig({ enabled: true, key: 'same-key', certificate: 'same-cert' }); + const different = createConfig({ enabled: false, key: 'same-key', certificate: 'same-cert' }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `key`', () => { + const reference = createEnabledConfig({ key: 'keyA', certificate: 'same-cert' }); + const same = createEnabledConfig({ key: 'keyA', certificate: 'same-cert' }); + const different = createEnabledConfig({ key: 'keyB', certificate: 'same-cert' }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `keyPassphrase`', () => { + const reference = createEnabledConfig({ keyPassphrase: 'passA' }); + const same = createEnabledConfig({ keyPassphrase: 'passA' }); + const different = createEnabledConfig({ keyPassphrase: 'passB' }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `certificate`', () => { + const reference = createEnabledConfig({ key: 'same-key', certificate: 'cert-a' }); + const same = createEnabledConfig({ key: 'same-key', certificate: 'cert-a' }); + const different = createEnabledConfig({ key: 'same-key', certificate: 'cert-b' }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `cipherSuites`', () => { + const reference = createEnabledConfig({ cipherSuites: ['A', 'B'] }); + const same = createEnabledConfig({ cipherSuites: ['A', 'B'] }); + const different = createEnabledConfig({ cipherSuites: ['A', 'C'] }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `supportedProtocols`', () => { + const reference = createEnabledConfig({ supportedProtocols: ['TLSv1.1', 'TLSv1.2'] }); + const same = createEnabledConfig({ supportedProtocols: ['TLSv1.1', 'TLSv1.2'] }); + const different = createEnabledConfig({ supportedProtocols: ['TLSv1.1', 'TLSv1.3'] }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + + it('compares `clientAuthentication`', () => { + const reference = createEnabledConfig({ clientAuthentication: 'none' }); + const same = createEnabledConfig({ clientAuthentication: 'none' }); + const different = createEnabledConfig({ clientAuthentication: 'required' }); + + expect(reference.isEqualTo(same)).toBe(true); + expect(reference.isEqualTo(different)).toBe(false); + }); + }); }); describe('#sslSchema', () => { diff --git a/packages/kbn-server-http-tools/src/ssl/ssl_config.ts b/packages/kbn-server-http-tools/src/ssl/ssl_config.ts index 53d3616a09a75..a5d43064baa6b 100644 --- a/packages/kbn-server-http-tools/src/ssl/ssl_config.ts +++ b/packages/kbn-server-http-tools/src/ssl/ssl_config.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { isEqual } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; import { readPkcs12Keystore, readPkcs12Truststore } from '@kbn/crypto'; import { constants as cryptoConstants } from 'crypto'; @@ -161,6 +162,13 @@ export class SslConfig { : secureOptions | secureOption; // eslint-disable-line no-bitwise }, 0); } + + public isEqualTo(otherConfig: SslConfig) { + if (this === otherConfig) { + return true; + } + return isEqual(this, otherConfig); + } } const readFile = (file: string) => readFileSync(file, 'utf8'); diff --git a/packages/kbn-subscription-tracking/README.md b/packages/kbn-subscription-tracking/README.md deleted file mode 100644 index 4f84593980881..0000000000000 --- a/packages/kbn-subscription-tracking/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# @kbn/subscription-tracking - -This package leverages the `@kbn/analytics-client` package to send dedicated subscription tracking events. - -It provides a set of React components that automatically track `impression` and `click` events. Consumers of those components need to specify a `subscription context` that gives more details on the type of feature that is advertised and the location of the upsell. - -```typescript -import { SubscriptionLink } from '@kbn/subscription-tracking'; -import type { SubscriptionContext } from '@kbn/subscription-tracking'; - -const subscriptionContext: SubscriptionContext = { - feature: 'threat-intelligence', - source: 'security__threat-intelligence', -}; - -export const Paywall = () => { - return ( -
- - Upgrade to Platinum to get this feature - -
- ); -}; -``` - -The example above uses a `SubscriptionLink` which is a wrapper of `EuiLink` . So it behaves just like a normal link. Alternatively, upsells can also use a `SubscriptionButton` or `SubscriptionButtonEmpty` which wrap `EuiButton` and `EuiButtonEmpty` respectively. - -When the link is mounted, it will send off an `impression` event with the given `subscriptionContext`. That piece of metadata consists of an identifier of the advertised feature (in this case `threat-intelligence`) and the `source` (aka location) of the impression (in this case the `threat-intelligence` page in the `security` solution). `source` follows the following format: `{solution-identifier}__location-identifier`. - -There are no special rules for how to name these identifiers but it's good practise to make sure that `feature` has the same value for all upsells advertising the same feature (e.g. use enums for features to prevent spelling mistakes). - -Upon interaction with the upsell link/button, a special `click` event is sent, which, again, contains the same subscription context. - -If you want to use the `subscription-tracking` elements in your app, you have to set up a `SubscriptionTrackingProvider` in your plugin setup and register the tracking events on startup. Have a look at https://github.com/elastic/kibana/pull/143910 for an example of an integration. diff --git a/packages/kbn-subscription-tracking/kibana.jsonc b/packages/kbn-subscription-tracking/kibana.jsonc deleted file mode 100644 index e165baebfa76b..0000000000000 --- a/packages/kbn-subscription-tracking/kibana.jsonc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "shared-common", - "id": "@kbn/subscription-tracking", - "owner": "@elastic/security-threat-hunting-investigations" -} diff --git a/packages/kbn-subscription-tracking/mocks.tsx b/packages/kbn-subscription-tracking/mocks.tsx deleted file mode 100644 index b918f9bba2828..0000000000000 --- a/packages/kbn-subscription-tracking/mocks.tsx +++ /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 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 } from 'react'; -import { analyticsClientMock } from '@kbn/analytics-client/src/mocks'; - -import { SubscriptionTrackingProvider } from './src/services'; - -const analyticsClientMockInst = analyticsClientMock.create(); - -/** - * Mock for the external services provider. Only use in tests! - */ -export const MockSubscriptionTrackingProvider: FC = ({ children }) => { - return ( - - {children} - - ); -}; diff --git a/packages/kbn-subscription-tracking/src/helpers.test.ts b/packages/kbn-subscription-tracking/src/helpers.test.ts deleted file mode 100644 index fa000567d35d3..0000000000000 --- a/packages/kbn-subscription-tracking/src/helpers.test.ts +++ /dev/null @@ -1,45 +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 { isValidContext } from './helpers'; - -describe('tracking', () => { - describe('isValidLocation', () => { - it('identifies correct contexts', () => { - expect( - isValidContext({ - feature: 'test', - source: 'security__test', - }) - ).toBeTruthy(); - }); - - it('identifies incorrect contexts', () => { - expect( - isValidContext({ - feature: '', - source: 'security__', - }) - ).toBeFalsy(); - - expect( - isValidContext({ - feature: 'test', - source: 'security__', - }) - ).toBeFalsy(); - - expect( - isValidContext({ - feature: '', - source: 'security__test', - }) - ).toBeFalsy(); - }); - }); -}); diff --git a/packages/kbn-subscription-tracking/src/services.tsx b/packages/kbn-subscription-tracking/src/services.tsx deleted file mode 100644 index 857bd0b0dcd89..0000000000000 --- a/packages/kbn-subscription-tracking/src/services.tsx +++ /dev/null @@ -1,70 +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, { FC, useContext } from 'react'; -import type { AnalyticsClient, EventTypeOpts } from '@kbn/analytics-client'; -import { EVENT_NAMES, Services, SubscriptionContextData } from '../types'; - -export const SubscriptionTrackingContext = React.createContext(null); - -/** - * External services provider - */ -export const SubscriptionTrackingProvider: FC = ({ children, ...services }) => { - return ( - - {children} - - ); -}; - -/** - * React hook for accessing pre-wired services. - */ -export function useServices() { - const context = useContext(SubscriptionTrackingContext); - - if (!context) { - throw new Error( - 'SubscriptionTrackingContext is missing. Ensure your component or React root is wrapped with SubscriptionTrackingProvider.' - ); - } - - return context; -} - -const subscriptionContextSchema: EventTypeOpts['schema'] = { - source: { - type: 'keyword', - _meta: { - description: - 'A human-readable identifier describing the location of the beginning of the subscription flow', - }, - }, - feature: { - type: 'keyword', - _meta: { - description: 'A human-readable identifier describing the feature that is being promoted', - }, - }, -}; - -/** - * Registers the subscription-specific event types - */ -export function registerEvents(analyticsClient: Pick) { - analyticsClient.registerEventType({ - eventType: EVENT_NAMES.IMPRESSION, - schema: subscriptionContextSchema, - }); - - analyticsClient.registerEventType({ - eventType: EVENT_NAMES.CLICK, - schema: subscriptionContextSchema, - }); -} diff --git a/packages/kbn-subscription-tracking/src/subscription_elements.test.tsx b/packages/kbn-subscription-tracking/src/subscription_elements.test.tsx deleted file mode 100644 index 1795bbf42dd0c..0000000000000 --- a/packages/kbn-subscription-tracking/src/subscription_elements.test.tsx +++ /dev/null @@ -1,108 +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 { render, screen } from '@testing-library/react'; -import { - SubscriptionLink, - SubscriptionButton, - SubscriptionButtonEmpty, -} from './subscription_elements'; -import { SubscriptionTrackingProvider } from './services'; -import { EVENT_NAMES, Services, SubscriptionContextData } from '../types'; -import { coolDownTimeMs, resetCoolDown } from './use_impression'; - -const testServices: Services = { - navigateToApp: jest.fn(), - analyticsClient: { - reportEvent: jest.fn(), - registerEventType: jest.fn(), - } as any, -}; -const testContext: SubscriptionContextData = { feature: 'test', source: 'security__test' }; - -const WithProviders: React.FC = ({ children }) => ( - - {children} - -); - -const renderWithProviders = (children: React.ReactElement) => - render(children, { wrapper: WithProviders }); - -const reset = () => { - jest.resetAllMocks(); - resetCoolDown(); -}; - -describe('SubscriptionElements', () => { - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - [SubscriptionButton, SubscriptionLink, SubscriptionButtonEmpty].forEach((SubscriptionElement) => { - describe(SubscriptionElement.name, () => { - beforeEach(reset); - - it('renders the children correctly', () => { - renderWithProviders( - Hello - ); - expect(screen.getByText('Hello')).toBeTruthy(); - }); - - it('fires an impression event when rendered', () => { - renderWithProviders(); - expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledWith( - EVENT_NAMES.IMPRESSION, - testContext - ); - }); - - it('fires an impression event when rendered (but only once)', () => { - const { unmount } = renderWithProviders( - - ); - expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(1); - unmount(); - - // does not create an impression again when remounted - const { unmount: unmountAgain } = renderWithProviders( - - ); - unmountAgain(); - expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(1); - - // only creates anew impression when the cooldown time has passed - jest.setSystemTime(Date.now() + coolDownTimeMs); - renderWithProviders(); - expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledTimes(2); - }); - - it('tracks a click when clicked and navigates to page', () => { - renderWithProviders( - hello - ); - - screen.getByText('hello').click(); - expect(testServices.analyticsClient.reportEvent).toHaveBeenCalledWith( - EVENT_NAMES.CLICK, - testContext - ); - expect(testServices.navigateToApp).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/packages/kbn-subscription-tracking/src/subscription_elements.tsx b/packages/kbn-subscription-tracking/src/subscription_elements.tsx deleted file mode 100644 index f29c58d8a0a41..0000000000000 --- a/packages/kbn-subscription-tracking/src/subscription_elements.tsx +++ /dev/null @@ -1,79 +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 { EuiLink, EuiButton, EuiButtonEmpty } from '@elastic/eui'; -import type { EuiLinkProps, EuiButtonEmptyProps, EuiButtonProps } from '@elastic/eui'; -import { useGoToSubscription } from './use_go_to_subscription'; -import { useImpression } from './use_impression'; -import type { SubscriptionContextData } from '../types'; - -interface CommonProps { - /** The context information for this subscription element */ - subscriptionContext: SubscriptionContextData; -} - -export type SubscriptionLinkProps = EuiLinkProps & CommonProps; - -/** - * Wrapper around `EuiLink` that provides subscription events - */ -export function SubscriptionLink({ - subscriptionContext, - children, - ...restProps -}: SubscriptionLinkProps) { - const goToSubscription = useGoToSubscription({ subscriptionContext }); - useImpression(subscriptionContext); - - return ( - - {children} - - ); -} - -export type SubscriptionButtonProps = EuiButtonProps & CommonProps; - -/** - * Wrapper around `EuiButton` that provides subscription events - */ -export function SubscriptionButton({ - subscriptionContext, - children, - ...restProps -}: SubscriptionButtonProps) { - const goToSubscription = useGoToSubscription({ subscriptionContext }); - useImpression(subscriptionContext); - - return ( - - {children} - - ); -} - -export type SubscriptionButtonEmptyProps = EuiButtonEmptyProps & CommonProps; - -/** - * Wrapper around `EuiButtonEmpty` that provides subscription events - */ -export function SubscriptionButtonEmpty({ - subscriptionContext, - children, - ...restProps -}: SubscriptionButtonEmptyProps) { - const goToSubscription = useGoToSubscription({ subscriptionContext }); - useImpression(subscriptionContext); - - return ( - - {children} - - ); -} diff --git a/packages/kbn-subscription-tracking/src/use_go_to_subscription.ts b/packages/kbn-subscription-tracking/src/use_go_to_subscription.ts deleted file mode 100644 index 6c93fb27ee9bd..0000000000000 --- a/packages/kbn-subscription-tracking/src/use_go_to_subscription.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 { useCallback } from 'react'; -import { isValidContext } from './helpers'; -import { useServices } from './services'; -import { EVENT_NAMES, SubscriptionContextData } from '../types'; - -interface Options { - subscriptionContext: SubscriptionContextData; -} - -/** - * Provides a navigation function that navigates to the subscription - * management page. When the function executes, a click event with the - * given context is emitted. - */ -export const useGoToSubscription = ({ subscriptionContext }: Options) => { - const { navigateToApp, analyticsClient } = useServices(); - const goToSubscription = useCallback(() => { - if (isValidContext(subscriptionContext)) { - analyticsClient.reportEvent(EVENT_NAMES.CLICK, subscriptionContext); - } else { - // eslint-disable-next-line no-console - console.error('The provided subscription context is invalid', subscriptionContext); - } - navigateToApp('management', { path: 'stack/license_management' }); - }, [analyticsClient, navigateToApp, subscriptionContext]); - - return goToSubscription; -}; diff --git a/packages/kbn-subscription-tracking/src/use_impression.ts b/packages/kbn-subscription-tracking/src/use_impression.ts deleted file mode 100644 index eb8aa4c2e0ec5..0000000000000 --- a/packages/kbn-subscription-tracking/src/use_impression.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { useEffect } from 'react'; -import { isValidContext } from './helpers'; -import { useServices } from './services'; -import { EVENT_NAMES, SubscriptionContextData } from '../types'; - -/** - * Sends an impression event with the given context. - * - * Note: impression events are throttled and will not fire more - * often than once every 30 seconds. - */ -export const useImpression = (context: SubscriptionContextData) => { - const { analyticsClient } = useServices(); - - useEffect(() => { - if (!isValidContext(context)) { - // eslint-disable-next-line no-console - console.error('The provided subscription context is invalid', context); - return; - } - if (!isCoolingDown(context)) { - analyticsClient.reportEvent(EVENT_NAMES.IMPRESSION, context); - coolDown(context); - } - }, [analyticsClient, context]); -}; - -/** - * Impressions from the same context should not fire more than once every 30 seconds. - * This prevents logging too many impressions in case a page is reloaded often or - * if the user is navigating back and forth rapidly. - */ -export const coolDownTimeMs = 30 * 1000; -let impressionCooldown = new WeakMap(); - -function isCoolingDown(context: SubscriptionContextData) { - const previousLog = impressionCooldown.get(context); - - // we logged before and we are in the cooldown period - return previousLog && Date.now() - previousLog < coolDownTimeMs; -} - -function coolDown(context: SubscriptionContextData) { - impressionCooldown.set(context, Date.now()); -} - -export function resetCoolDown() { - impressionCooldown = new WeakMap(); -} diff --git a/packages/kbn-subscription-tracking/types.ts b/packages/kbn-subscription-tracking/types.ts deleted file mode 100644 index a2adf0c6d90c5..0000000000000 --- a/packages/kbn-subscription-tracking/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -import type { AnalyticsClient } from '@kbn/analytics-client'; - -enum SolutionIdentifier { - observability = 'observability', - security = 'security', -} -type LocationString = string; -type SourceIdentifier = `${SolutionIdentifier}__${LocationString}`; -/** - * A piece of metadata which consists of an identifier of the advertised feature and - * the `source` (e.g. location) of the subscription element. - */ -export interface SubscriptionContextData { - /** - * A human-readable identifier describing the location of the beginning of the - * subscription flow. - * Location identifiers are prefixed with a solution identifier, e.g. `security__` - * - * @example "security__host-overview" - the user is looking at an upsell button - * on the host overview page in the security solution - */ - source: SourceIdentifier; - - /** - * A human-readable identifier describing the feature that is being promoted. - * - * @example "alerts-by-process-ancestry" - */ - feature: string; -} - -export interface Services { - navigateToApp: (app: string, options: { path: string }) => void; - analyticsClient: Pick; -} - -export enum EVENT_NAMES { - CLICK = 'subscription__upsell__click', - IMPRESSION = 'subscription__upsell__impression', -} diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index 461ad2b6f0df1..84c2da1e52f80 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -71,7 +71,10 @@ export interface CreateTestEsClusterOptions { */ esArgs?: string[]; esFrom?: string; - esServerlessOptions?: Pick; + esServerlessOptions?: Pick< + ServerlessOptions, + 'image' | 'tag' | 'resources' | 'host' | 'kibanaUrl' + >; esJavaOpts?: string; /** * License to run your cluster under. Keep in mind that a `trial` license @@ -242,10 +245,7 @@ export function createTestEsCluster< await firstNode.runServerless({ basePath, esArgs: customEsArgs, - image: esServerlessOptions?.image, - tag: esServerlessOptions?.tag, - host: esServerlessOptions?.host, - resources: esServerlessOptions?.resources, + ...esServerlessOptions, port, clean: true, background: true, diff --git a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts index 742f729745d27..4f01321b82391 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import Url from 'url'; import { resolve } from 'path'; import type { ToolingLog } from '@kbn/tooling-log'; import getPort from 'get-port'; @@ -160,7 +161,18 @@ async function startEsNode({ return cluster; } -function getESServerlessOptions(esServerlessImageFromArg: string | undefined, config: Config) { +interface EsServerlessOptions { + host?: string; + resources: string[]; + kibanaUrl: string; + tag?: string; + image?: string; +} + +function getESServerlessOptions( + esServerlessImageFromArg: string | undefined, + config: Config +): EsServerlessOptions { const esServerlessImageUrlOrTag = esServerlessImageFromArg || esTestConfig.getESServerlessImage() || @@ -172,24 +184,24 @@ function getESServerlessOptions(esServerlessImageFromArg: string | undefined, co const serverlessHost: string | undefined = config.has('esServerlessOptions.host') && config.get('esServerlessOptions.host'); + const commonOptions = { + host: serverlessHost, + resources: serverlessResources, + kibanaUrl: Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }), + }; + if (esServerlessImageUrlOrTag) { - if (esServerlessImageUrlOrTag.includes(':')) { - return { - resources: serverlessResources, - image: esServerlessImageUrlOrTag, - host: serverlessHost, - }; - } else { - return { - resources: serverlessResources, - tag: esServerlessImageUrlOrTag, - host: serverlessHost, - }; - } + return { + ...commonOptions, + ...(esServerlessImageUrlOrTag.includes(':') + ? { image: esServerlessImageUrlOrTag } + : { tag: esServerlessImageUrlOrTag }), + }; } - return { - resources: serverlessResources, - host: serverlessHost, - }; + return commonOptions; } diff --git a/packages/kbn-text-based-editor/src/editor_footer.tsx b/packages/kbn-text-based-editor/src/editor_footer.tsx index 5070e2d5789e7..6bc663713a8d0 100644 --- a/packages/kbn-text-based-editor/src/editor_footer.tsx +++ b/packages/kbn-text-based-editor/src/editor_footer.tsx @@ -19,6 +19,9 @@ import { EuiPopoverTitle, EuiDescriptionList, EuiDescriptionListDescription, + EuiButton, + useEuiTheme, + EuiLink, } from '@elastic/eui'; import { Interpolation, Theme, css } from '@emotion/react'; import { css as classNameCss } from '@emotion/css'; @@ -27,6 +30,7 @@ import type { MonacoError } from './helpers'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; const COMMAND_KEY = isMac ? '⌘' : '^'; +const FEEDBACK_LINK = 'https://ela.st/esql-feedback'; const getConstsByType = (type: 'error' | 'warning', count: number) => { if (type === 'error') { @@ -54,18 +58,51 @@ const getConstsByType = (type: 'error' | 'warning', count: number) => { } }; +export function SubmitFeedbackComponent({ isSpaceReduced }: { isSpaceReduced?: boolean }) { + const { euiTheme } = useEuiTheme(); + return ( + <> + + + + + + {isSpaceReduced + ? i18n.translate('textBasedEditor.query.textBasedLanguagesEditor.feedback', { + defaultMessage: 'Feedback', + }) + : i18n.translate('textBasedEditor.query.textBasedLanguagesEditor.submitFeedback', { + defaultMessage: 'Submit feedback', + })} + + + + ); +} + export function ErrorsWarningsPopover({ isPopoverOpen, items, type, setIsPopoverOpen, onErrorClick, + isSpaceReduced, }: { isPopoverOpen: boolean; items: MonacoError[]; type: 'error' | 'warning'; setIsPopoverOpen: (flag: boolean) => void; onErrorClick: (error: MonacoError) => void; + isSpaceReduced?: boolean; }) { const strings = getConstsByType(type, items.length); return ( @@ -90,7 +127,7 @@ export function ErrorsWarningsPopover({ setIsPopoverOpen(!isPopoverOpen); }} > -

{strings.message}

+

{isSpaceReduced ? items.length : strings.message}

} ownFocus={false} @@ -151,8 +188,11 @@ interface EditorFooterProps { warning?: MonacoError[]; detectTimestamp: boolean; onErrorClick: (error: MonacoError) => void; - refreshErrors: () => void; + runQuery: () => void; hideRunQueryText?: boolean; + disableSubmitAction?: boolean; + editorIsInline?: boolean; + isSpaceReduced?: boolean; } export const EditorFooter = memo(function EditorFooter({ @@ -162,10 +202,15 @@ export const EditorFooter = memo(function EditorFooter({ warning, detectTimestamp, onErrorClick, - refreshErrors, + runQuery, hideRunQueryText, + disableSubmitAction, + editorIsInline, + isSpaceReduced, }: EditorFooterProps) { + const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); + return ( + + +

+ {i18n.translate('textBasedEditor.query.textBasedLanguagesEditor.lineCount', { + defaultMessage: '{count} {count, plural, one {line} other {lines}}', + values: { count: lines }, + })} +

+
+
+ {/* If there is no space and no @timestamp detected hide the information */} + {(detectTimestamp || !isSpaceReduced) && ( + + + + +

+ {isSpaceReduced + ? '@timestamp' + : detectTimestamp + ? i18n.translate( + 'textBasedEditor.query.textBasedLanguagesEditor.timestampDetected', + { + defaultMessage: '@timestamp found', + } + ) + : i18n.translate( + 'textBasedEditor.query.textBasedLanguagesEditor.timestampNotDetected', + { + defaultMessage: '@timestamp not found', + } + )} +

+
+
+
+
+ )} {errors && errors.length > 0 && ( )} {warning && warning.length > 0 && ( @@ -192,49 +276,15 @@ export const EditorFooter = memo(function EditorFooter({ type="warning" setIsPopoverOpen={setIsPopoverOpen} onErrorClick={onErrorClick} + isSpaceReduced={isSpaceReduced} /> )} - - -

- {i18n.translate('textBasedEditor.query.textBasedLanguagesEditor.lineCount', { - defaultMessage: '{count} {count, plural, one {line} other {lines}}', - values: { count: lines }, - })} -

-
-
- - - - - - - -

- {detectTimestamp - ? i18n.translate( - 'textBasedEditor.query.textBasedLanguagesEditor.timestampDetected', - { - defaultMessage: '@timestamp detected', - } - ) - : i18n.translate( - 'textBasedEditor.query.textBasedLanguagesEditor.timestampNotDetected', - { - defaultMessage: '@timestamp not detected', - } - )} -

-
-
-
-
{!hideRunQueryText && ( +

@@ -255,6 +305,63 @@ export const EditorFooter = memo(function EditorFooter({ )} + {Boolean(editorIsInline) && ( + <> + + + + + + + + {isSpaceReduced + ? i18n.translate('textBasedEditor.query.textBasedLanguagesEditor.run', { + defaultMessage: 'Run', + }) + : i18n.translate( + 'textBasedEditor.query.textBasedLanguagesEditor.runQuery', + { + defaultMessage: 'Run query', + } + )} + + + + {COMMAND_KEY}⏎ + + + + + + + + + )} ); }); diff --git a/packages/kbn-text-based-editor/src/resizable_button.tsx b/packages/kbn-text-based-editor/src/resizable_button.tsx index fb4ee944bc2f5..5a52d67780ca6 100644 --- a/packages/kbn-text-based-editor/src/resizable_button.tsx +++ b/packages/kbn-text-based-editor/src/resizable_button.tsx @@ -13,11 +13,13 @@ import { css } from '@emotion/react'; export function ResizableButton({ onMouseDownResizeHandler, onKeyDownResizeHandler, + editorIsInline, }: { onMouseDownResizeHandler: ( mouseDownEvent: React.MouseEvent | React.TouchEvent ) => void; onKeyDownResizeHandler: (keyDownEvernt: React.KeyboardEvent) => void; + editorIsInline?: boolean; }) { return ( { let position = isCompactFocused ? ('absolute' as 'absolute') : ('relative' as 'relative'); // cast string to type 'relative' | 'absolute' if (isCodeEditorExpanded) { @@ -33,7 +34,9 @@ export const textBasedLanguagedEditorStyles = ( zIndex: isCompactFocused ? 4 : 0, height: `${editorHeight}px`, border: isCompactFocused ? euiTheme.border.thin : 'none', - borderTopLeftRadius: isCodeEditorExpanded ? 0 : '6px', + borderLeft: editorIsInline || !isCompactFocused ? 'none' : euiTheme.border.thin, + borderRight: editorIsInline || !isCompactFocused ? 'none' : euiTheme.border.thin, + borderTopLeftRadius: isCodeEditorExpanded ? 0 : euiTheme.border.radius.medium, borderBottom: isCodeEditorExpanded ? 'none' : isCompactFocused @@ -45,8 +48,8 @@ export const textBasedLanguagedEditorStyles = ( width: isCodeEditorExpanded ? '100%' : `calc(100% - ${hasReference ? 80 : 40}px)`, alignItems: isCompactFocused ? 'flex-start' : 'center', border: !isCompactFocused ? euiTheme.border.thin : 'none', - borderTopLeftRadius: '6px', - borderBottomLeftRadius: '6px', + borderTopLeftRadius: euiTheme.border.radius.medium, + borderBottomLeftRadius: euiTheme.border.radius.medium, borderBottomWidth: hasErrors ? '2px' : '1px', borderBottomColor: hasErrors ? euiTheme.colors.danger : euiTheme.colors.lightShade, }, @@ -66,6 +69,8 @@ export const textBasedLanguagedEditorStyles = ( }, bottomContainer: { border: euiTheme.border.thin, + borderLeft: editorIsInline ? 'none' : euiTheme.border.thin, + borderRight: editorIsInline ? 'none' : euiTheme.border.thin, borderTop: isCodeEditorExpanded && !isCodeEditorExpandedFocused ? hasErrors @@ -75,29 +80,29 @@ export const textBasedLanguagedEditorStyles = ( backgroundColor: euiTheme.colors.lightestShade, paddingLeft: euiTheme.size.base, paddingRight: euiTheme.size.base, - paddingTop: euiTheme.size.xs, - paddingBottom: euiTheme.size.xs, + paddingTop: editorIsInline ? euiTheme.size.s : euiTheme.size.xs, + paddingBottom: editorIsInline ? euiTheme.size.s : euiTheme.size.xs, width: 'calc(100% + 2px)', position: 'relative' as 'relative', // cast string to type 'relative', marginTop: 0, marginLeft: 0, marginBottom: 0, - borderBottomLeftRadius: '6px', - borderBottomRightRadius: '6px', + borderBottomLeftRadius: editorIsInline ? 0 : euiTheme.border.radius.medium, + borderBottomRightRadius: editorIsInline ? 0 : euiTheme.border.radius.medium, }, topContainer: { - border: euiTheme.border.thin, - borderTopLeftRadius: '6px', - borderTopRightRadius: '6px', + border: editorIsInline ? 'none' : euiTheme.border.thin, + borderTopLeftRadius: editorIsInline ? 0 : euiTheme.border.radius.medium, + borderTopRightRadius: editorIsInline ? 0 : euiTheme.border.radius.medium, backgroundColor: euiTheme.colors.lightestShade, paddingLeft: euiTheme.size.base, paddingRight: euiTheme.size.base, - paddingTop: euiTheme.size.xs, - paddingBottom: euiTheme.size.xs, + paddingTop: editorIsInline ? euiTheme.size.s : euiTheme.size.xs, + paddingBottom: editorIsInline ? euiTheme.size.s : euiTheme.size.xs, width: 'calc(100% + 2px)', position: 'relative' as 'relative', // cast string to type 'relative', marginLeft: 0, - marginTop: euiTheme.size.s, + marginTop: editorIsInline ? 0 : euiTheme.size.s, }, dragResizeContainer: { width: '100%', diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.test.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.test.tsx index 0be4c38eed749..0f9ed5c6cb9c5 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.test.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.test.tsx @@ -69,46 +69,49 @@ describe('TextBasedLanguagesEditor', () => { }; }); it('should render the editor component', async () => { - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); - expect(component.find('[data-test-subj="TextBasedLangEditor"]').length).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); + expect(component.find('[data-test-subj="TextBasedLangEditor"]').length).not.toBe(0); }); it('should render the lines badge for the inline mode by default', async () => { - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); - expect( - component.find('[data-test-subj="TextBasedLangEditor-inline-lines-badge"]').length - ).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); + expect( + component.find('[data-test-subj="TextBasedLangEditor-inline-lines-badge"]').length + ).not.toBe(0); }); - it('should render the date info with no @timestamp detected', async () => { + it('should render the date info with no @timestamp found', async () => { const newProps = { ...props, isCodeEditorExpanded: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect( - component.find('[data-test-subj="TextBasedLangEditor-date-info"]').at(0).text() - ).toStrictEqual('@timestamp not detected'); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="TextBasedLangEditor-date-info"]').at(0).text() + ).toStrictEqual('@timestamp not found'); }); - it('should render the date info with @timestamp detected if detectTimestamp is true', async () => { + it('should render the feedback link', async () => { + const newProps = { + ...props, + isCodeEditorExpanded: true, + }; + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-feedback-link"]').length).not.toBe( + 0 + ); + }); + + it('should render the date info with @timestamp found if detectTimestamp is true', async () => { const newProps = { ...props, isCodeEditorExpanded: true, detectTimestamp: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect( - component.find('[data-test-subj="TextBasedLangEditor-date-info"]').at(0).text() - ).toStrictEqual('@timestamp detected'); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="TextBasedLangEditor-date-info"]').at(0).text() + ).toStrictEqual('@timestamp found'); }); it('should render the errors badge for the inline mode by default if errors are provides', async () => { @@ -116,12 +119,10 @@ describe('TextBasedLanguagesEditor', () => { ...props, errors: [new Error('error1')], }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect( - component.find('[data-test-subj="TextBasedLangEditor-inline-errors-badge"]').length - ).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="TextBasedLangEditor-inline-errors-badge"]').length + ).not.toBe(0); }); it('should render the warnings badge for the inline mode by default if warning are provides', async () => { @@ -129,12 +130,10 @@ describe('TextBasedLanguagesEditor', () => { ...props, warning: 'Line 1: 20: Warning', }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect( - component.find('[data-test-subj="TextBasedLangEditor-inline-warning-badge"]').length - ).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="TextBasedLangEditor-inline-warning-badge"]').length + ).not.toBe(0); }); it('should render the correct buttons for the inline code editor mode', async () => { @@ -156,11 +155,9 @@ describe('TextBasedLanguagesEditor', () => { ...props, expandCodeEditor: expandCodeEditorSpy, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - findTestSubject(component, 'TextBasedLangEditor-expand').simulate('click'); - expect(expandCodeEditorSpy).toHaveBeenCalled(); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + findTestSubject(component, 'TextBasedLangEditor-expand').simulate('click'); + expect(expandCodeEditorSpy).toHaveBeenCalled(); }); it('should render the correct buttons for the expanded code editor mode', async () => { @@ -211,11 +208,9 @@ describe('TextBasedLanguagesEditor', () => { isCodeEditorExpanded: true, expandCodeEditor: expandCodeEditorSpy, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - findTestSubject(component, 'TextBasedLangEditor-minimize').simulate('click'); - expect(expandCodeEditorSpy).toHaveBeenCalled(); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + findTestSubject(component, 'TextBasedLangEditor-minimize').simulate('click'); + expect(expandCodeEditorSpy).toHaveBeenCalled(); }); it('should render the resize for the expanded code editor mode', async () => { @@ -223,10 +218,8 @@ describe('TextBasedLanguagesEditor', () => { ...props, isCodeEditorExpanded: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect(component.find('[data-test-subj="TextBasedLangEditor-resize"]').length).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-resize"]').length).not.toBe(0); }); it('should render the footer for the expanded code editor mode', async () => { @@ -234,13 +227,11 @@ describe('TextBasedLanguagesEditor', () => { ...props, isCodeEditorExpanded: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect(component.find('[data-test-subj="TextBasedLangEditor-footer"]').length).not.toBe(0); - expect( - component.find('[data-test-subj="TextBasedLangEditor-footer-lines"]').at(0).text() - ).toBe('1 line'); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-footer"]').length).not.toBe(0); + expect(component.find('[data-test-subj="TextBasedLangEditor-footer-lines"]').at(0).text()).toBe( + '1 line' + ); }); it('should render the run query text', async () => { @@ -248,10 +239,8 @@ describe('TextBasedLanguagesEditor', () => { ...props, isCodeEditorExpanded: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect(component.find('[data-test-subj="TextBasedLangEditor-run-query"]').length).not.toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-run-query"]').length).not.toBe(0); }); it('should not render the run query text if the hideRunQueryText prop is set to true', async () => { @@ -260,9 +249,25 @@ describe('TextBasedLanguagesEditor', () => { isCodeEditorExpanded: true, hideRunQueryText: true, }; - await act(async () => { - const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); - expect(component.find('[data-test-subj="TextBasedLangEditor-run-query"]').length).toBe(0); - }); + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-run-query"]').length).toBe(0); + }); + + it('should render correctly if editorIsInline prop is set to true', async () => { + const onTextLangQuerySubmit = jest.fn(); + const newProps = { + ...props, + isCodeEditorExpanded: true, + hideRunQueryText: true, + editorIsInline: true, + onTextLangQuerySubmit, + }; + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="TextBasedLangEditor-run-query"]').length).toBe(0); + expect( + component.find('[data-test-subj="TextBasedLangEditor-run-query-button"]').length + ).not.toBe(1); + findTestSubject(component, 'TextBasedLangEditor-run-query-button').simulate('click'); + expect(onTextLangQuerySubmit).toHaveBeenCalled(); }); }); diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index 312d08cadf0c2..9d175ef86ecf0 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -65,19 +65,43 @@ import { fetchFieldsFromESQL } from './fetch_fields_from_esql'; import './overwrite.scss'; export interface TextBasedLanguagesEditorProps { + /** The aggregate type query */ query: AggregateQuery; + /** Callback running everytime the query changes */ onTextLangQueryChange: (query: AggregateQuery) => void; - onTextLangQuerySubmit: () => void; + /** Callback running when the user submits the query */ + onTextLangQuerySubmit: (query?: AggregateQuery) => void; + /** Can be used to expand/minimize the editor */ expandCodeEditor: (status: boolean) => void; + /** If it is true, the editor initializes with height EDITOR_INITIAL_HEIGHT_EXPANDED */ isCodeEditorExpanded: boolean; + /** If it is true, the editor displays the message @timestamp found + * The text based queries are relying on adhoc dataviews which + * can have an @timestamp timefield or nothing + */ detectTimestamp?: boolean; + /** Array of errors */ errors?: Error[]; + /** Warning string as it comes from ES */ warning?: string; + /** Disables the editor */ isDisabled?: boolean; + /** Indicator if the editor is on dark mode */ isDarkMode?: boolean; dataTestSubj?: string; + /** If true it hides the minimize button and the user can't return to the minimized version + * Useful when the application doesn't want to give this capability + */ hideMinimizeButton?: boolean; + /** Hide the Run query information which appears on the footer*/ hideRunQueryText?: boolean; + /** This is used for applications (such as the inline editing flyout in dashboards) + * which want to add the editor without being part of the Unified search component + * It renders a submit query button inside the editor + */ + editorIsInline?: boolean; + /** Disables the submit query action*/ + disableSubmitAction?: boolean; } interface TextBasedEditorDeps { @@ -94,6 +118,9 @@ const EDITOR_ONE_LINER_UNUSED_SPACE_WITH_ERRORS = 220; const KEYCODE_ARROW_UP = 38; const KEYCODE_ARROW_DOWN = 40; +// for editor width smaller than this value we want to start hiding some text +const BREAKPOINT_WIDTH = 540; + const languageId = (language: string) => { switch (language) { case 'esql': { @@ -125,6 +152,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ isDarkMode, hideMinimizeButton, hideRunQueryText, + editorIsInline, + disableSubmitAction, dataTestSubj, }: TextBasedLanguagesEditorProps) { const { euiTheme } = useEuiTheme(); @@ -137,6 +166,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const [editorHeight, setEditorHeight] = useState( isCodeEditorExpanded ? EDITOR_INITIAL_HEIGHT_EXPANDED : EDITOR_INITIAL_HEIGHT ); + const [isSpaceReduced, setIsSpaceReduced] = useState(false); const [showLineNumbers, setShowLineNumbers] = useState(isCodeEditorExpanded); const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded); const [isCodeEditorExpandedFocused, setIsCodeEditorExpandedFocused] = useState(false); @@ -166,7 +196,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ Boolean(errors?.length), Boolean(warning), isCodeEditorExpandedFocused, - Boolean(documentationSections) + Boolean(documentationSections), + Boolean(editorIsInline) ); const isDark = isDarkMode; const editorModel = useRef(); @@ -216,6 +247,11 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ [editorHeight] ); + const onQuerySubmit = useCallback(() => { + const currentValue = editor1.current?.getValue(); + onTextLangQuerySubmit({ [language]: currentValue } as AggregateQuery); + }, [language, onTextLangQuerySubmit]); + const restoreInitialMode = () => { setIsCodeEditorExpandedFocused(false); if (isCodeEditorExpanded) return; @@ -355,6 +391,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ }, [code, isCodeEditorExpanded, isWordWrapped]); const onResize = ({ width }: { width: number }) => { + setIsSpaceReduced(Boolean(editorIsInline && width < BREAKPOINT_WIDTH)); calculateVisibleCode(width); if (editor1.current) { editor1.current.layout({ width, height: editorHeight }); @@ -514,6 +551,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ { expandCodeEditor(false); updateLinesFromModel = false; @@ -582,8 +621,10 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )}

@@ -782,6 +826,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ language={ String(language) === 'esql' ? 'ES|QL' : String(language).toUpperCase() } + searchInDescription sections={documentationSections} buttonProps={{ display: 'empty', @@ -816,15 +861,19 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ errors={editorErrors} warning={editorWarning} onErrorClick={onErrorClick} - refreshErrors={onTextLangQuerySubmit} + runQuery={onQuerySubmit} detectTimestamp={detectTimestamp} hideRunQueryText={hideRunQueryText} + editorIsInline={editorIsInline} + disableSubmitAction={disableSubmitAction} + isSpaceReduced={isSpaceReduced} /> )} {isCodeEditorExpanded && ( )} diff --git a/packages/kbn-ui-actions-browser/index.ts b/packages/kbn-ui-actions-browser/index.ts index 9662013cf99e3..076b77680826b 100644 --- a/packages/kbn-ui-actions-browser/index.ts +++ b/packages/kbn-ui-actions-browser/index.ts @@ -15,7 +15,5 @@ export { visualizeGeoFieldTrigger, ROW_CLICK_TRIGGER, rowClickTrigger, - CATEGORIZE_FIELD_TRIGGER, - categorizeFieldTrigger, defaultTrigger, } from './src/triggers'; diff --git a/packages/kbn-ui-actions-browser/src/triggers/index.ts b/packages/kbn-ui-actions-browser/src/triggers/index.ts index 433a9cd6dff40..091305791d858 100644 --- a/packages/kbn-ui-actions-browser/src/triggers/index.ts +++ b/packages/kbn-ui-actions-browser/src/triggers/index.ts @@ -11,4 +11,3 @@ export * from './row_click_trigger'; export * from './default_trigger'; export * from './visualize_field_trigger'; export * from './visualize_geo_field_trigger'; -export * from './categorize_field_trigger'; diff --git a/packages/kbn-unified-data-table/__mocks__/data_view_without_timefield.ts b/packages/kbn-unified-data-table/__mocks__/data_view_without_timefield.ts new file mode 100644 index 0000000000000..cc07103a54486 --- /dev/null +++ b/packages/kbn-unified-data-table/__mocks__/data_view_without_timefield.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { DataView } from '@kbn/data-views-plugin/public'; +import { buildDataViewMock } from '@kbn/discover-utils/src/__mocks__'; + +const fields = [ + { + name: '_index', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: 'timestamp', + displayName: 'timestamp', + type: 'date', + scripted: false, + filterable: true, + aggregatable: true, + sortable: true, + }, + { + name: 'message', + displayName: 'message', + type: 'string', + scripted: false, + filterable: false, + }, + { + name: 'extension', + displayName: 'extension', + type: 'string', + scripted: false, + filterable: true, + aggregatable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + scripted: false, + filterable: true, + aggregatable: true, + }, + { + name: 'scripted', + displayName: 'scripted', + type: 'number', + scripted: true, + filterable: false, + }, +] as DataView['fields']; + +export const dataViewWithoutTimefieldMock = buildDataViewMock({ + name: 'index-pattern-without-timefield', + fields, + timeFieldName: undefined, +}); diff --git a/packages/kbn-unified-data-table/src/components/data_table.tsx b/packages/kbn-unified-data-table/src/components/data_table.tsx index 4ce88e52e6c8d..1f05601fd6892 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table.tsx @@ -56,7 +56,12 @@ import { getDisplayedColumns } from '../utils/columns'; import { convertValueToString } from '../utils/convert_value_to_string'; import { getRowsPerPageOptions } from '../utils/rows_per_page'; import { getRenderCellValueFn } from '../utils/get_render_cell_value'; -import { getEuiGridColumns, getLeadControlColumns, getVisibleColumns } from './data_table_columns'; +import { + getEuiGridColumns, + getLeadControlColumns, + getVisibleColumns, + hasSourceTimeFieldValue, +} from './data_table_columns'; import { UnifiedDataTableContext } from '../table_context'; import { getSchemaDetectors } from './data_table_schema'; import { DataTableDocumentToolbarBtn } from './data_table_document_selection'; @@ -617,9 +622,15 @@ export const UnifiedDataTable = ({ [dataView, onFieldEdited, services.dataViewFieldEditor] ); + const shouldShowTimeField = useMemo( + () => + hasSourceTimeFieldValue(displayedColumns, dataView, columnTypes, showTimeCol, isPlainRecord), + [dataView, displayedColumns, isPlainRecord, showTimeCol, columnTypes] + ); + const visibleColumns = useMemo( - () => getVisibleColumns(displayedColumns, dataView, showTimeCol), - [dataView, displayedColumns, showTimeCol] + () => getVisibleColumns(displayedColumns, dataView, shouldShowTimeField), + [dataView, displayedColumns, shouldShowTimeField] ); const getCellValue = useCallback( diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx index 38cbdb5aeb63a..e1d7082353e1c 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx @@ -7,8 +7,14 @@ */ import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; -import { getEuiGridColumns, getVisibleColumns } from './data_table_columns'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { + getEuiGridColumns, + getVisibleColumns, + hasSourceTimeFieldValue, +} from './data_table_columns'; import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield'; +import { dataViewWithoutTimefieldMock } from '../../__mocks__/data_view_without_timefield'; import { dataTableContextMock } from '../../__mocks__/table_context'; import { servicesMock } from '../../__mocks__/services'; @@ -110,6 +116,133 @@ describe('Data table columns', function () { }); }); + describe('hasSourceTimeFieldValue', () => { + function buildColumnTypes(dataView: DataView) { + const columnTypes: Record = {}; + for (const field of dataView.fields) { + columnTypes[field.name] = ''; + } + return columnTypes; + } + + describe('dataView with timeField', () => { + it('should forward showTimeCol if no _source columns is passed', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['extension', 'message'], + dataViewWithTimefieldMock, + buildColumnTypes(dataViewWithTimefieldMock), + showTimeCol, + false + ) + ).toBe(showTimeCol); + } + }); + + it('should forward showTimeCol if no _source columns is passed, text-based datasource', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['extension', 'message'], + dataViewWithTimefieldMock, + buildColumnTypes(dataViewWithTimefieldMock), + showTimeCol, + true + ) + ).toBe(showTimeCol); + } + }); + + it('should forward showTimeCol if _source column is passed', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['_source'], + dataViewWithTimefieldMock, + buildColumnTypes(dataViewWithTimefieldMock), + showTimeCol, + false + ) + ).toBe(showTimeCol); + } + }); + + it('should return true if _source column is passed, text-based datasource', () => { + // ... | DROP @timestamp test case + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['_source'], + dataViewWithTimefieldMock, + buildColumnTypes(dataViewWithTimefieldMock), + showTimeCol, + true + ) + ).toBe(true); + } + }); + }); + + describe('dataView without timeField', () => { + it('should forward showTimeCol if no _source columns is passed', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['extension', 'message'], + dataViewWithoutTimefieldMock, + buildColumnTypes(dataViewWithoutTimefieldMock), + showTimeCol, + false + ) + ).toBe(showTimeCol); + } + }); + + it('should forward showTimeCol if no _source columns is passed, text-based datasource', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['extension', 'message'], + dataViewWithoutTimefieldMock, + buildColumnTypes(dataViewWithoutTimefieldMock), + showTimeCol, + true + ) + ).toBe(showTimeCol); + } + }); + + it('should forward showTimeCol if _source column is passed', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['_source'], + dataViewWithoutTimefieldMock, + buildColumnTypes(dataViewWithoutTimefieldMock), + showTimeCol, + false + ) + ).toBe(showTimeCol); + } + }); + + it('should return false if _source column is passed, text-based datasource', () => { + for (const showTimeCol of [true, false]) { + expect( + hasSourceTimeFieldValue( + ['_source'], + dataViewWithoutTimefieldMock, + buildColumnTypes(dataViewWithoutTimefieldMock), + showTimeCol, + true + ) + ).toBe(showTimeCol); + } + }); + }); + }); + describe('column tokens', () => { it('returns eui grid columns with tokens', async () => { const actual = getEuiGridColumns({ diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx index 274b1148df4eb..f7a3d41bf330e 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx @@ -267,6 +267,20 @@ export function getEuiGridColumns({ ); } +export function hasSourceTimeFieldValue( + columns: string[], + dataView: DataView, + columnTypes: DataTableColumnTypes | undefined, + showTimeCol: boolean, + isPlainRecord: boolean +) { + const timeFieldName = dataView.timeFieldName; + if (!isPlainRecord || !columns.includes('_source') || !timeFieldName || !columnTypes) { + return showTimeCol; + } + return timeFieldName in columnTypes; +} + export function getVisibleColumns(columns: string[], dataView: DataView, showTimeCol: boolean) { const timeFieldName = dataView.timeFieldName; diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts b/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts index afeb34e6572af..007a88b2c7f97 100644 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts +++ b/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import type { UiActionsStart, CategorizeFieldContext } from '@kbn/ui-actions-plugin/public'; -import { CATEGORIZE_FIELD_TRIGGER } from '@kbn/ui-actions-browser/src/triggers'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { CATEGORIZE_FIELD_TRIGGER, type CategorizeFieldContext } from '@kbn/ml-ui-actions'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; async function getCompatibleActions( diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx b/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx index 4e0d00b4549ca..45569b3443370 100644 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx +++ b/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx @@ -14,9 +14,12 @@ import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/ import { ActionInternal } from '@kbn/ui-actions-plugin/public'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { getFieldCategorizeButton } from './field_categorize_button'; -import { ACTION_CATEGORIZE_FIELD, CategorizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import { + CATEGORIZE_FIELD_TRIGGER, + ACTION_CATEGORIZE_FIELD, + type CategorizeFieldContext, +} from '@kbn/ml-ui-actions'; import { TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; -import { CATEGORIZE_FIELD_TRIGGER } from '@kbn/ui-actions-browser'; const ORIGINATING_APP = 'test'; const mockExecuteAction = jest.fn(); diff --git a/packages/kbn-unified-field-list/tsconfig.json b/packages/kbn-unified-field-list/tsconfig.json index 27cbb23cf3d7c..eeca808e1bee7 100644 --- a/packages/kbn-unified-field-list/tsconfig.json +++ b/packages/kbn-unified-field-list/tsconfig.json @@ -31,6 +31,7 @@ "@kbn/ebt-tools", "@kbn/shared-ux-button-toolbar", "@kbn/field-utils", + "@kbn/ml-ui-actions", ], "exclude": ["target/**/*"] } diff --git a/packages/kbn-url-state/README.md b/packages/kbn-url-state/README.md index e7b131e3743d5..3549936888f9f 100644 --- a/packages/kbn-url-state/README.md +++ b/packages/kbn-url-state/README.md @@ -1,45 +1,33 @@ # @kbn/url-state - utils for syncing state to URL -This package provides: +This package provides a React hook called `useUrlState` that can be used to synchronize state to the URL. This can be useful when you want to make a portion of state shareable. -- a React hook called `useSyncToUrl` that can be used to synchronize state to the URL. This can be useful when you want to make a portion of state shareable. - -## useSyncToUrl - -The `useSyncToUrl` hook takes three arguments: - -``` -key (string): The key to use in the URL to store the state. -restore (function): A function that is called with the deserialized value from the URL. You should use this function to update your state based on the value from the URL. -cleanupOnHistoryNavigation (optional boolean, default: true): If true, the hook will clear the URL state when the user navigates using the browser's history API. -``` +The state is grouped under a namespace, to avoid collisions. See the example url below for how it would look like. ### Example usage: ``` import React, { useState } from 'react'; -import { useSyncToUrl } from '@kbn/url-state'; +import { useUrlState } from '@kbn/url-state'; function MyComponent() { - const [count, setCount] = useState(0); - - useSyncToUrl('count', (value) => { - setCount(value); - }); + const [name, setName] = useUrlState('namespace','name'); const handleClick = () => { - setCount((prevCount) => prevCount + 1); + setName('John Doe') }; return (
-

Count: {count}

- +

Name: {name}

+
); } ``` -In this example, the count state is synced to the URL using the `useSyncToUrl` hook. -Whenever the count state changes, the hook will update the URL with the new value. -When the user copies the updated url or refreshes the page, `restore` function will be called to update the count state. \ No newline at end of file +The resulting URL will look like this: + +``` +http://localhost:5601/?namespace=(name:John%20Doe) +``` diff --git a/packages/kbn-url-state/index.test.ts b/packages/kbn-url-state/index.test.ts index e2a85e58902f1..7e646a387d245 100644 --- a/packages/kbn-url-state/index.test.ts +++ b/packages/kbn-url-state/index.test.ts @@ -7,8 +7,7 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; -import { useSyncToUrl } from '.'; -import { encode } from '@kbn/rison'; +import { useUrlState } from '.'; describe('useSyncToUrl', () => { let originalLocation: Location; @@ -28,95 +27,70 @@ describe('useSyncToUrl', () => { window.history = { ...originalHistory, replaceState: jest.fn(), + pushState: jest.fn(), }; + + jest.useFakeTimers(); }); afterEach(() => { window.location = originalLocation; window.history = originalHistory; + jest.useRealTimers(); }); - it('should restore the value from the query string on mount', () => { - const key = 'testKey'; - const restoredValue = { test: 'value' }; - const encodedValue = encode(restoredValue); - const restore = jest.fn(); - - window.location.search = `?${key}=${encodedValue}`; - - renderHook(() => useSyncToUrl(key, restore)); - - expect(restore).toHaveBeenCalledWith(restoredValue); - }); - - it('should sync the value to the query string', () => { - const key = 'testKey'; - const valueToSerialize = { test: 'value' }; + it('should update the URL when the state changes', () => { + window.location.hash = '#should_be_there'; - const { result } = renderHook(() => useSyncToUrl(key, jest.fn())); + const { result } = renderHook(() => useUrlState('namespace', 'test')); act(() => { - result.current(valueToSerialize); + result.current[1]('foo'); + jest.runAllTimers(); }); - expect(window.history.replaceState).toHaveBeenCalledWith( - { path: expect.any(String) }, + expect(window.history.pushState).toHaveBeenCalledWith( + {}, '', - '/?testKey=%28test%3Avalue%29' + '#should_be_there?namespace=(test:foo)' ); }); - it('should should not alter the location hash', () => { - const key = 'testKey'; - const valueToSerialize = { test: 'value' }; + it('should remove the key from the namespace after undefined is passed (state clear mechanism)', () => { window.location.hash = '#should_be_there'; - const { result } = renderHook(() => useSyncToUrl(key, jest.fn())); + const { result } = renderHook(() => useUrlState('namespace', 'test')); act(() => { - result.current(valueToSerialize); + result.current[1](undefined); + jest.runAllTimers(); }); - expect(window.history.replaceState).toHaveBeenCalledWith( - { path: expect.any(String) }, - '', - '/#should_be_there?testKey=%28test%3Avalue%29' - ); + expect(window.history.pushState).toHaveBeenCalledWith({}, '', '#should_be_there?namespace=()'); }); - it('should clear the value from the query string on unmount', () => { - const key = 'testKey'; - - // Location should have a key to clear - window.location.search = `?${key}=${encode({ test: 'value' })}`; - - const { unmount } = renderHook(() => useSyncToUrl(key, jest.fn())); + it('should restore the value from the query string on mount', () => { + window.location.search = `?namespace=(test:foo)`; - act(() => { - unmount(); - }); + const { + result: { current: state }, + } = renderHook(() => useUrlState('namespace', 'test')); - expect(window.history.replaceState).toHaveBeenCalledWith( - { path: expect.any(String) }, - '', - expect.any(String) - ); + expect(state[0]).toEqual('foo'); }); - it('should clear the value from the query string when history back or forward is pressed', () => { - const key = 'testKey'; - const restore = jest.fn(); + it('should return updated state on browser navigation', () => { + window.location.search = '?namespace=(test:foo)'; - // Location should have a key to clear - window.location.search = `?${key}=${encode({ test: 'value' })}`; + const { result } = renderHook(() => useUrlState('namespace', 'test')); - renderHook(() => useSyncToUrl(key, restore, true)); + expect(result.current[0]).toEqual('foo'); act(() => { - window.dispatchEvent(new Event('popstate')); + window.location.search = '?namespace=(test:bar)'; + window.dispatchEvent(new CustomEvent('popstate')); }); - expect(window.history.replaceState).toHaveBeenCalledTimes(1); - expect(window.history.replaceState).toHaveBeenCalledWith({ path: expect.any(String) }, '', '/'); + expect(result.current[0]).toEqual('bar'); }); }); diff --git a/packages/kbn-url-state/index.ts b/packages/kbn-url-state/index.ts index 73568222fb4c0..d165d75e1f956 100644 --- a/packages/kbn-url-state/index.ts +++ b/packages/kbn-url-state/index.ts @@ -6,4 +6,118 @@ * Side Public License, v 1. */ -export { useSyncToUrl } from './use_sync_to_url'; +import { useCallback, useEffect, useState } from 'react'; +import { encode, decode, RisonValue } from '@kbn/rison'; +import { stringify, parse } from 'query-string'; + +interface StateCache { + namespaces: Record>; + timeoutHandle: number; +} + +/** + * Temporary cache for state stored in the URL. This will be serialized to the URL + * in a single batched update to avoid excessive history entries. + */ +const cache: StateCache = { + namespaces: {}, + timeoutHandle: 0, +}; + +const CUSTOM_URL_EVENT = 'url:update' as const; + +// This is a list of events that can trigger a render. +const URL_CHANGE_EVENTS: string[] = ['popstate', CUSTOM_URL_EVENT]; + +/** + * This hook stores state in the URL, but with a namespace to avoid collisions with other values in the URL. + * It also batches updates to the URL to avoid excessive history entries. + * With it, you can store state in the URL and have it persist across page refreshes. + * The state is stored in the URL as a Rison encoded object. + * + * Example: when called like this `const [value, setValue] = useUrlState('myNamespace', 'myKey');` + * the state will be stored in the URL like this: `?myNamespace=(myKey:!n)` + * + * State is not cleared from the URL when the hook is unmounted and this is by design. + * If you want it to be cleared, you can do it manually by calling `setValue(undefined)`. + * + * @param urlNamespace actual top level query param key + * @param key sub key of the query param + */ +export const useUrlState = (urlNamespace: string, key: string) => { + if (!cache.namespaces[urlNamespace]) { + cache.namespaces[urlNamespace] = {}; + } + + const [internalValue, setInternalValue] = useState(undefined); + + useEffect(() => { + // This listener is called on browser navigation or on custom event. + // It updates the LOCAL state, allowing dependent components to re-render. + const listener = () => { + const searchParams = new URLSearchParams(window.location.search); + const param = searchParams.get(urlNamespace); + + const decodedState = param ? decode(param) : ({} as Record); + const decodedValue = (decodedState as Record | undefined)?.[key]; + cache.namespaces[urlNamespace][key] = decodedValue; + setInternalValue(decodedValue as unknown as T); + }; + + listener(); + + URL_CHANGE_EVENTS.forEach((event) => window.addEventListener(event, listener)); + + return () => URL_CHANGE_EVENTS.forEach((event) => window.removeEventListener(event, listener)); + }, [key, urlNamespace]); + + const setValue = useCallback( + (updatedValue: T | undefined) => { + const currentValue = cache.namespaces[urlNamespace][key]; + + const canSpread = + typeof updatedValue === 'object' && + typeof currentValue === 'object' && + !Array.isArray(updatedValue) && + !Array.isArray(currentValue); + + cache.namespaces[urlNamespace][key] = canSpread + ? ({ ...currentValue, ...updatedValue } as unknown as T) + : (updatedValue as unknown as T); + + // This batches updates to the URL state to avoid excessive history entries + if (cache.timeoutHandle) { + window.clearTimeout(cache.timeoutHandle); + } + + // The push state call is delayed to make sure that multiple calls to setValue + // within a short period of time are batched together. + cache.timeoutHandle = window.setTimeout(() => { + const searchParams = parse(location.search); + + for (const ns in cache.namespaces) { + if (!Object.prototype.hasOwnProperty.call(cache.namespaces, ns)) { + continue; + } + searchParams[ns] = encode(cache.namespaces[ns]); + } + + const newSearch = stringify(searchParams, { encode: false }); + + if (window.location.search === newSearch) { + return; + } + + const newUrl = `${window.location.hash}?${newSearch}`; + + window.history.pushState({}, '', newUrl); + // This custom event is used to notify other instances + // of this hook that the URL has changed. + window.dispatchEvent(new Event(CUSTOM_URL_EVENT)); + }, 0); + }, + [key, urlNamespace] + ); + + return [internalValue, setValue] as const; +}; diff --git a/packages/kbn-url-state/use_sync_to_url.ts b/packages/kbn-url-state/use_sync_to_url.ts deleted file mode 100644 index e6f1531980f75..0000000000000 --- a/packages/kbn-url-state/use_sync_to_url.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { useCallback, useEffect } from 'react'; -import { encode, decode } from '@kbn/rison'; - -// https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event -const POPSTATE_EVENT = 'popstate' as const; - -/** - * Sync any object with browser query string using @knb/rison - * @param key query string param to use - * @param restore use this to handle restored state - * @param cleanupOnHistoryNavigation use history events to cleanup state on back / forward naviation. true by default - */ -export const useSyncToUrl = ( - key: string, - restore: (data: TValueToSerialize) => void, - cleanupOnHistoryNavigation = true -) => { - useEffect(() => { - const params = new URLSearchParams(window.location.search); - const param = params.get(key); - - if (!param) { - return; - } - - const decodedQuery = decode(param); - - if (!decodedQuery) { - return; - } - - // Only restore the value if it is not falsy - restore(decodedQuery as unknown as TValueToSerialize); - }, [key, restore]); - - /** - * Synces value with the url state, under specified key. If payload is undefined, the value will be removed from the query string althogether. - */ - const syncValueToQueryString = useCallback( - (valueToSerialize?: TValueToSerialize) => { - const searchParams = new URLSearchParams(window.location.search); - - if (valueToSerialize) { - const serializedPayload = encode(valueToSerialize); - searchParams.set(key, serializedPayload); - } else { - searchParams.delete(key); - } - - const stringifiedSearchParams = searchParams.toString(); - const newSearch = stringifiedSearchParams.length > 0 ? `?${stringifiedSearchParams}` : ''; - - if (window.location.search === newSearch) { - return; - } - - // Update query string without unnecessary re-render - const newUrl = `${window.location.pathname}${window.location.hash}${newSearch}`; - window.history.replaceState({ path: newUrl }, '', newUrl); - }, - [key] - ); - - // Clear remove state from the url on unmount / when history back or forward is pressed - useEffect(() => { - const clearState = () => { - syncValueToQueryString(undefined); - }; - - if (cleanupOnHistoryNavigation) { - window.addEventListener(POPSTATE_EVENT, clearState); - } - - return () => { - clearState(); - - if (cleanupOnHistoryNavigation) { - window.removeEventListener(POPSTATE_EVENT, clearState); - } - }; - }, [cleanupOnHistoryNavigation, syncValueToQueryString]); - - return syncValueToQueryString; -}; diff --git a/packages/kbn-utils/src/path/index.test.ts b/packages/kbn-utils/src/path/index.test.ts index 608f3cb2cfeb2..163dfbef598dc 100644 --- a/packages/kbn-utils/src/path/index.test.ts +++ b/packages/kbn-utils/src/path/index.test.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ +import { join } from 'path'; import { accessSync, constants } from 'fs'; -import { getConfigPath, getDataPath, getLogsPath, getConfigDirectory } from '.'; +import { rm, mkdtemp, writeFile } from 'fs/promises'; +import { getConfigPath, getDataPath, getLogsPath, getConfigDirectory, buildDataPaths } from '.'; import { REPO_ROOT } from '@kbn/repo-info'; expect.addSnapshotSerializer( @@ -46,3 +48,97 @@ describe('Default path finder', () => { expect(() => accessSync(configPath, constants.R_OK)).not.toThrow(); }); }); + +describe('Custom data path finder', () => { + const originalArgv = process.argv; + + beforeEach(() => { + process.argv = originalArgv; + }); + + it('ignores the path.data flag when no value is provided', () => { + process.argv = ['--foo', 'bar', '--path.data', '--baz', 'xyz']; + + expect(buildDataPaths()).toMatchInlineSnapshot(` + Array [ + /data, + "/var/lib/kibana", + ] + `); + }); + + describe('overrides path.data when provided as command line argument', () => { + it('with absolute path', () => { + process.argv = ['--foo', 'bar', '--path.data', '/some/data/path', '--baz', 'xyz']; + + /* + * Test buildDataPaths since getDataPath returns the first valid directory and + * custom paths do not exist in environment. Custom directories are built during env init. + */ + expect(buildDataPaths()).toMatchInlineSnapshot(` + Array [ + "/some/data/path", + /data, + "/var/lib/kibana", + ] + `); + }); + + it('with relative path', () => { + process.argv = ['--foo', 'bar', '--path.data', 'data2', '--baz', 'xyz']; + + /* + * Test buildDataPaths since getDataPath returns the first valid directory and + * custom paths do not exist in environment. Custom directories are built during env init. + */ + expect(buildDataPaths()).toMatchInlineSnapshot(` + Array [ + /data2, + /data, + "/var/lib/kibana", + ] + `); + }); + }); + + describe('overrides path.data when provided by kibana.yml', () => { + let tempDir: string; + let tempConfigFile: string; + + beforeAll(async () => { + tempDir = await mkdtemp('config-test'); + tempConfigFile = join(tempDir, 'kibana.yml'); + }); + + afterAll(async () => { + await rm(tempDir, { recursive: true }); + delete process.env.KBN_PATH_CONF; + }); + + it('with absolute path', async () => { + process.env.KBN_PATH_CONF = tempDir; + await writeFile(tempConfigFile, `path.data: /path/from/yml`); + + expect(buildDataPaths()).toMatchInlineSnapshot(` + Array [ + "/path/from/yml", + /data, + "/var/lib/kibana", + ] + `); + }); + + it('with relative path', async () => { + process.env.KBN_PATH_CONF = tempDir; + await writeFile(tempConfigFile, `path.data: data2`); + + expect(buildDataPaths()).toMatchInlineSnapshot(` + Array [ + /data2, + /data, + "/var/lib/kibana", + ] + `); + }); + }); +}); diff --git a/packages/kbn-utils/src/path/index.ts b/packages/kbn-utils/src/path/index.ts index 63ca454dd04fd..caeaa9a493f17 100644 --- a/packages/kbn-utils/src/path/index.ts +++ b/packages/kbn-utils/src/path/index.ts @@ -6,18 +6,22 @@ * Side Public License, v 1. */ -import { join } from 'path'; +import { join, resolve } from 'path'; import { accessSync, constants } from 'fs'; import { TypeOf, schema } from '@kbn/config-schema'; import { REPO_ROOT } from '@kbn/repo-info'; +import { getConfigFromFiles } from '@kbn/config'; +import getopts from 'getopts'; const isString = (v: any): v is string => typeof v === 'string'; -const CONFIG_PATHS = [ - process.env.KBN_PATH_CONF && join(process.env.KBN_PATH_CONF, 'kibana.yml'), - join(REPO_ROOT, 'config/kibana.yml'), - '/etc/kibana/kibana.yml', -].filter(isString); +const buildConfigPaths = () => { + return [ + process.env.KBN_PATH_CONF && resolve(process.env.KBN_PATH_CONF, 'kibana.yml'), + join(REPO_ROOT, 'config/kibana.yml'), + '/etc/kibana/kibana.yml', + ].filter(isString); +}; const CONFIG_DIRECTORIES = [ process.env.KBN_PATH_CONF, @@ -25,8 +29,6 @@ const CONFIG_DIRECTORIES = [ '/etc/kibana', ].filter(isString); -const DATA_PATHS = [join(REPO_ROOT, 'data'), '/var/lib/kibana'].filter(isString); - const LOGS_PATHS = [join(REPO_ROOT, 'logs'), '/var/log/kibana'].filter(isString); function findFile(paths: string[]) { @@ -41,11 +43,29 @@ function findFile(paths: string[]) { return availablePath || paths[0]; } +export const buildDataPaths = (): string[] => { + const configDataPath = getConfigFromFiles([getConfigPath()]).path?.data; + const argv = process.argv.slice(2); + const options = getopts(argv, { + string: ['pathData'], + alias: { + pathData: 'path.data', + }, + }); + + return [ + !!options.pathData && resolve(REPO_ROOT, options.pathData), + configDataPath && resolve(REPO_ROOT, configDataPath), + join(REPO_ROOT, 'data'), + '/var/lib/kibana', + ].filter(isString); +}; + /** * Get the path of kibana.yml * @internal */ -export const getConfigPath = () => findFile(CONFIG_PATHS); +export const getConfigPath = () => findFile(buildConfigPaths()); /** * Get the directory containing configuration files @@ -57,7 +77,7 @@ export const getConfigDirectory = () => findFile(CONFIG_DIRECTORIES); * Get the directory containing runtime data * @internal */ -export const getDataPath = () => findFile(DATA_PATHS); +export const getDataPath = () => findFile(buildDataPaths()); /** * Get the directory containing logs diff --git a/packages/kbn-utils/tsconfig.json b/packages/kbn-utils/tsconfig.json index 6baa222ef8c37..f6e7fb408bfa2 100644 --- a/packages/kbn-utils/tsconfig.json +++ b/packages/kbn-utils/tsconfig.json @@ -13,6 +13,7 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/repo-info", + "@kbn/config", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-visualization-ui-components/components/field_picker/field_picker.test.tsx b/packages/kbn-visualization-ui-components/components/field_picker/field_picker.test.tsx new file mode 100644 index 0000000000000..1b821dd44bc93 --- /dev/null +++ b/packages/kbn-visualization-ui-components/components/field_picker/field_picker.test.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { FieldPicker, FieldPickerProps } from './field_picker'; +import { render, screen } from '@testing-library/react'; +import faker from 'faker'; +import userEvent from '@testing-library/user-event'; +import { DataType, FieldOptionValue } from './types'; + +const generateFieldWithLabelOfLength = (length: number) => ({ + label: faker.random.alpha({ count: length }), + value: { + type: 'field' as const, + field: faker.random.alpha({ count: length }), + dataType: 'date' as DataType, + operationType: 'count', + }, + exists: true, + compatible: 1, +}); + +const generateProps = (customField = generateFieldWithLabelOfLength(20)) => + ({ + selectedOptions: [ + { + label: 'Category', + value: { + type: 'field' as const, + field: 'category.keyword', + dataType: 'keyword' as DataType, + operationType: 'count', + }, + }, + ], + options: [ + { + label: 'nested options', + exists: true, + compatible: 1, + value: generateFieldWithLabelOfLength(20), + options: [ + generateFieldWithLabelOfLength(20), + customField, + generateFieldWithLabelOfLength(20), + ], + }, + ], + onChoose: jest.fn(), + fieldIsInvalid: false, + } as unknown as FieldPickerProps); + +describe('field picker', () => { + const renderFieldPicker = (customField = generateFieldWithLabelOfLength(20)) => { + const props = generateProps(customField); + const rtlRender = render(); + return { + openCombobox: () => userEvent.click(screen.getByLabelText(/open list of options/i)), + ...rtlRender, + }; + }; + + it('should render minimum width dropdown list if all labels are short', async () => { + const { openCombobox } = renderFieldPicker(); + openCombobox(); + const popover = screen.getByRole('dialog'); + expect(popover).toHaveStyle('inline-size: 256px'); + }); + + it('should render calculated width dropdown list if the longest label is longer than min width', async () => { + const { openCombobox } = renderFieldPicker(generateFieldWithLabelOfLength(50)); + openCombobox(); + + const popover = screen.getByRole('dialog'); + expect(popover).toHaveStyle('inline-size: 466px'); + }); + + it('should render maximum width dropdown list if the longest label is longer than max width', async () => { + const { openCombobox } = renderFieldPicker(generateFieldWithLabelOfLength(80)); + openCombobox(); + const popover = screen.getByRole('dialog'); + expect(popover).toHaveStyle('inline-size: 550px'); + }); +}); diff --git a/packages/kbn-visualization-ui-components/components/field_picker/field_picker.tsx b/packages/kbn-visualization-ui-components/components/field_picker/field_picker.tsx index 5b6022d5cb454..237b7c85cd8fd 100644 --- a/packages/kbn-visualization-ui-components/components/field_picker/field_picker.tsx +++ b/packages/kbn-visualization-ui-components/components/field_picker/field_picker.tsx @@ -9,9 +9,10 @@ import './field_picker.scss'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import classNames from 'classnames'; import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui'; import { FieldIcon } from '@kbn/field-utils/src/components/field_icon'; -import classNames from 'classnames'; +import { calculateWidthFromCharCount } from '@kbn/calculate-width-from-char-count'; import type { FieldOptionValue, FieldOption } from './types'; export interface FieldPickerProps @@ -27,23 +28,26 @@ export interface FieldPickerProps const MIDDLE_TRUNCATION_PROPS = { truncation: 'middle' as const }; const SINGLE_SELECTION_AS_TEXT_PROPS = { asPlainText: true }; -export function FieldPicker({ - selectedOptions, - options, - onChoose, - onDelete, - fieldIsInvalid, - ['data-test-subj']: dataTestSub, - ...rest -}: FieldPickerProps) { - let theLongestLabel = ''; +export function FieldPicker( + props: FieldPickerProps +) { + const { + selectedOptions, + options, + onChoose, + onDelete, + fieldIsInvalid, + ['data-test-subj']: dataTestSub, + ...rest + } = props; + let maxLabelLength = 0; const styledOptions = options?.map(({ compatible, exists, ...otherAttr }) => { if (otherAttr.options) { return { ...otherAttr, options: otherAttr.options.map(({ exists: fieldOptionExists, ...fieldOption }) => { - if (fieldOption.label.length > theLongestLabel.length) { - theLongestLabel = fieldOption.label; + if (fieldOption.label.length > maxLabelLength) { + maxLabelLength = fieldOption.label.length; } return { ...fieldOption, @@ -75,7 +79,6 @@ export function FieldPicker({ }; }); - const panelMinWidth = getPanelMinWidth(theLongestLabel.length); return ( ({ selectedOptions={selectedOptions} singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS} truncationProps={MIDDLE_TRUNCATION_PROPS} - inputPopoverProps={{ panelMinWidth }} + inputPopoverProps={{ + panelMinWidth: calculateWidthFromCharCount(maxLabelLength), + anchorPosition: 'downRight', + }} onChange={(choices) => { if (choices.length === 0) { onDelete?.(); @@ -102,20 +108,3 @@ export function FieldPicker({ /> ); } - -const MINIMUM_POPOVER_WIDTH = 300; -const MINIMUM_POPOVER_WIDTH_CHAR_COUNT = 28; -const AVERAGE_CHAR_WIDTH = 7; -const MAXIMUM_POPOVER_WIDTH_CHAR_COUNT = 60; -const MAXIMUM_POPOVER_WIDTH = 550; // fitting 60 characters - -function getPanelMinWidth(labelLength: number) { - if (labelLength > MAXIMUM_POPOVER_WIDTH_CHAR_COUNT) { - return MAXIMUM_POPOVER_WIDTH; - } - if (labelLength > MINIMUM_POPOVER_WIDTH_CHAR_COUNT) { - const overflownChars = labelLength - MINIMUM_POPOVER_WIDTH_CHAR_COUNT; - return MINIMUM_POPOVER_WIDTH + overflownChars * AVERAGE_CHAR_WIDTH; - } - return MINIMUM_POPOVER_WIDTH; -} diff --git a/packages/kbn-visualization-ui-components/tsconfig.json b/packages/kbn-visualization-ui-components/tsconfig.json index 78f0b8a4b111f..a9d6627828dc7 100644 --- a/packages/kbn-visualization-ui-components/tsconfig.json +++ b/packages/kbn-visualization-ui-components/tsconfig.json @@ -31,5 +31,6 @@ "@kbn/coloring", "@kbn/field-formats-plugin", "@kbn/field-utils", + "@kbn/calculate-width-from-char-count" ], } diff --git a/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap b/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap index 65ed8b80aa8a3..d8a1b5d6eacc3 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap +++ b/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap @@ -5,21 +5,15 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": undefined, "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.item1", "sideNavStatus": "visible", "title": "Item 1", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "", "href": "", @@ -27,19 +21,14 @@ Array [ "title": "Title from deeplink!", "url": "", }, - "href": undefined, + "href": "", "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item2", - ], + "isElasticInternalLink": false, + "path": "group1.item2", "sideNavStatus": "visible", "title": "Title from deeplink!", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "", "href": "", @@ -47,14 +36,10 @@ Array [ "title": "Title from deeplink!", "url": "", }, - "href": undefined, + "href": "", "id": "item3", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item3", - ], + "isElasticInternalLink": false, + "path": "group1.item3", "sideNavStatus": "visible", "title": "Deeplink title overriden", }, @@ -62,11 +47,8 @@ Array [ "deepLink": undefined, "href": undefined, "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - ], + "isElasticInternalLink": false, + "path": "group1", "sideNavStatus": "visible", "title": "Group 1", "type": "navGroup", @@ -74,7 +56,6 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/discover", @@ -82,19 +63,14 @@ Array [ "title": "Deeplink discover", "url": "/mocked/discover", }, - "href": undefined, + "href": "/mocked/discover", "id": "discover", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:analytics", - "discover", - ], + "isElasticInternalLink": false, + "path": "rootNav:analytics.discover", "sideNavStatus": "visible", "title": "Deeplink discover", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/dashboards", @@ -102,19 +78,14 @@ Array [ "title": "Deeplink dashboards", "url": "/mocked/dashboards", }, - "href": undefined, + "href": "/mocked/dashboards", "id": "dashboards", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:analytics", - "dashboards", - ], + "isElasticInternalLink": false, + "path": "rootNav:analytics.dashboards", "sideNavStatus": "visible", "title": "Deeplink dashboards", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/visualize", @@ -122,14 +93,10 @@ Array [ "title": "Deeplink visualize", "url": "/mocked/visualize", }, - "href": undefined, + "href": "/mocked/visualize", "id": "visualize", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:analytics", - "visualize", - ], + "isElasticInternalLink": false, + "path": "rootNav:analytics.visualize", "sideNavStatus": "visible", "title": "Deeplink visualize", }, @@ -138,11 +105,8 @@ Array [ "href": undefined, "icon": "stats", "id": "rootNav:analytics", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:analytics", - ], + "isElasticInternalLink": false, + "path": "rootNav:analytics", "renderAs": "accordion", "sideNavStatus": "visible", "title": "Data exploration", @@ -151,7 +115,6 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:overview", @@ -159,19 +122,14 @@ Array [ "title": "Deeplink ml:overview", "url": "/mocked/ml:overview", }, - "href": undefined, + "href": "/mocked/ml:overview", "id": "ml:overview", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:overview", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:overview", "sideNavStatus": "visible", "title": "Deeplink ml:overview", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:notifications", @@ -179,19 +137,14 @@ Array [ "title": "Deeplink ml:notifications", "url": "/mocked/ml:notifications", }, - "href": undefined, + "href": "/mocked/ml:notifications", "id": "ml:notifications", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:notifications", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:notifications", "sideNavStatus": "visible", "title": "Deeplink ml:notifications", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:memoryUsage", @@ -199,14 +152,10 @@ Array [ "title": "Deeplink ml:memoryUsage", "url": "/mocked/ml:memoryUsage", }, - "href": undefined, + "href": "/mocked/ml:memoryUsage", "id": "ml:memoryUsage", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:memoryUsage", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:memoryUsage", "sideNavStatus": "visible", "title": "Deeplink ml:memoryUsage", }, @@ -214,7 +163,6 @@ Array [ "children": Array [ Object { "breadcrumbStatus": "hidden", - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:anomalyDetection", @@ -222,20 +170,14 @@ Array [ "title": "Deeplink ml:anomalyDetection", "url": "/mocked/ml:anomalyDetection", }, - "href": undefined, + "href": "/mocked/ml:anomalyDetection", "id": "ml:anomalyDetection", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:anomalyDetection", - "ml:anomalyDetection", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:anomalyDetection.ml:anomalyDetection", "sideNavStatus": "visible", "title": "Jobs", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:anomalyExplorer", @@ -243,20 +185,14 @@ Array [ "title": "Deeplink ml:anomalyExplorer", "url": "/mocked/ml:anomalyExplorer", }, - "href": undefined, + "href": "/mocked/ml:anomalyExplorer", "id": "ml:anomalyExplorer", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:anomalyDetection", - "ml:anomalyExplorer", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:anomalyDetection.ml:anomalyExplorer", "sideNavStatus": "visible", "title": "Deeplink ml:anomalyExplorer", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:singleMetricViewer", @@ -264,20 +200,14 @@ Array [ "title": "Deeplink ml:singleMetricViewer", "url": "/mocked/ml:singleMetricViewer", }, - "href": undefined, + "href": "/mocked/ml:singleMetricViewer", "id": "ml:singleMetricViewer", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:anomalyDetection", - "ml:singleMetricViewer", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:anomalyDetection.ml:singleMetricViewer", "sideNavStatus": "visible", "title": "Deeplink ml:singleMetricViewer", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:settings", @@ -285,15 +215,10 @@ Array [ "title": "Deeplink ml:settings", "url": "/mocked/ml:settings", }, - "href": undefined, + "href": "/mocked/ml:settings", "id": "ml:settings", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:anomalyDetection", - "ml:settings", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:anomalyDetection.ml:settings", "sideNavStatus": "visible", "title": "Deeplink ml:settings", }, @@ -305,14 +230,10 @@ Array [ "title": "Deeplink ml:anomalyDetection", "url": "/mocked/ml:anomalyDetection", }, - "href": undefined, + "href": "/mocked/ml:anomalyDetection", "id": "ml:anomalyDetection", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - "ml:anomalyDetection", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:anomalyDetection", "renderAs": "accordion", "sideNavStatus": "visible", "title": "Anomaly Detection", @@ -321,7 +242,6 @@ Array [ "children": Array [ Object { "breadcrumbStatus": "hidden", - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:dataFrameAnalytics", @@ -329,20 +249,14 @@ Array [ "title": "Deeplink ml:dataFrameAnalytics", "url": "/mocked/ml:dataFrameAnalytics", }, - "href": undefined, + "href": "/mocked/ml:dataFrameAnalytics", "id": "ml:dataFrameAnalytics", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:dataFrameAnalytics", - "ml:dataFrameAnalytics", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:dataFrameAnalytics.ml:dataFrameAnalytics", "sideNavStatus": "visible", "title": "Jobs", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:resultExplorer", @@ -350,20 +264,14 @@ Array [ "title": "Deeplink ml:resultExplorer", "url": "/mocked/ml:resultExplorer", }, - "href": undefined, + "href": "/mocked/ml:resultExplorer", "id": "ml:resultExplorer", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:dataFrameAnalytics", - "ml:resultExplorer", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:dataFrameAnalytics.ml:resultExplorer", "sideNavStatus": "visible", "title": "Deeplink ml:resultExplorer", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:analyticsMap", @@ -371,15 +279,10 @@ Array [ "title": "Deeplink ml:analyticsMap", "url": "/mocked/ml:analyticsMap", }, - "href": undefined, + "href": "/mocked/ml:analyticsMap", "id": "ml:analyticsMap", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "ml:dataFrameAnalytics", - "ml:analyticsMap", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:dataFrameAnalytics.ml:analyticsMap", "sideNavStatus": "visible", "title": "Deeplink ml:analyticsMap", }, @@ -391,14 +294,10 @@ Array [ "title": "Deeplink ml:dataFrameAnalytics", "url": "/mocked/ml:dataFrameAnalytics", }, - "href": undefined, + "href": "/mocked/ml:dataFrameAnalytics", "id": "ml:dataFrameAnalytics", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - "ml:dataFrameAnalytics", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.ml:dataFrameAnalytics", "renderAs": "accordion", "sideNavStatus": "visible", "title": "Data Frame Analytics", @@ -406,7 +305,6 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:nodesOverview", @@ -414,20 +312,14 @@ Array [ "title": "Deeplink ml:nodesOverview", "url": "/mocked/ml:nodesOverview", }, - "href": undefined, + "href": "/mocked/ml:nodesOverview", "id": "ml:nodesOverview", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "model_management", - "ml:nodesOverview", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.model_management.ml:nodesOverview", "sideNavStatus": "visible", "title": "Deeplink ml:nodesOverview", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:nodes", @@ -435,15 +327,10 @@ Array [ "title": "Deeplink ml:nodes", "url": "/mocked/ml:nodes", }, - "href": undefined, + "href": "/mocked/ml:nodes", "id": "ml:nodes", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "model_management", - "ml:nodes", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.model_management.ml:nodes", "sideNavStatus": "visible", "title": "Deeplink ml:nodes", }, @@ -451,12 +338,8 @@ Array [ "deepLink": undefined, "href": undefined, "id": "model_management", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - "model_management", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.model_management", "renderAs": "accordion", "sideNavStatus": "visible", "title": "Model Management", @@ -464,7 +347,6 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:fileUpload", @@ -472,20 +354,14 @@ Array [ "title": "Deeplink ml:fileUpload", "url": "/mocked/ml:fileUpload", }, - "href": undefined, + "href": "/mocked/ml:fileUpload", "id": "ml:fileUpload", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "data_visualizer", - "ml:fileUpload", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.data_visualizer.ml:fileUpload", "sideNavStatus": "visible", "title": "File", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:indexDataVisualizer", @@ -494,20 +370,14 @@ Array [ "url": "/mocked/ml:indexDataVisualizer", }, "getIsActive": [Function], - "href": undefined, + "href": "/mocked/ml:indexDataVisualizer", "id": "ml:indexDataVisualizer", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "data_visualizer", - "ml:indexDataVisualizer", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.data_visualizer.ml:indexDataVisualizer", "sideNavStatus": "visible", "title": "Data view", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:dataDrift", @@ -516,15 +386,10 @@ Array [ "url": "/mocked/ml:dataDrift", }, "getIsActive": [Function], - "href": undefined, + "href": "/mocked/ml:dataDrift", "id": "ml:dataDrift", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "data_visualizer", - "ml:dataDrift", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.data_visualizer.ml:dataDrift", "sideNavStatus": "visible", "title": "Data drift", }, @@ -532,12 +397,8 @@ Array [ "deepLink": undefined, "href": undefined, "id": "data_visualizer", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - "data_visualizer", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.data_visualizer", "renderAs": "accordion", "sideNavStatus": "visible", "title": "Data Visualizer", @@ -545,7 +406,6 @@ Array [ Object { "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:logRateAnalysis", @@ -554,20 +414,14 @@ Array [ "url": "/mocked/ml:logRateAnalysis", }, "getIsActive": [Function], - "href": undefined, + "href": "/mocked/ml:logRateAnalysis", "id": "ml:logRateAnalysis", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "aiops_labs", - "ml:logRateAnalysis", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.aiops_labs.ml:logRateAnalysis", "sideNavStatus": "visible", "title": "Deeplink ml:logRateAnalysis", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:logPatternAnalysis", @@ -576,20 +430,14 @@ Array [ "url": "/mocked/ml:logPatternAnalysis", }, "getIsActive": [Function], - "href": undefined, + "href": "/mocked/ml:logPatternAnalysis", "id": "ml:logPatternAnalysis", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "aiops_labs", - "ml:logPatternAnalysis", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.aiops_labs.ml:logPatternAnalysis", "sideNavStatus": "visible", "title": "Deeplink ml:logPatternAnalysis", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/ml:changePointDetections", @@ -598,15 +446,10 @@ Array [ "url": "/mocked/ml:changePointDetections", }, "getIsActive": [Function], - "href": undefined, + "href": "/mocked/ml:changePointDetections", "id": "ml:changePointDetections", - "isActive": false, - "isGroup": false, - "path": Array [ - "rootNav:ml", - "aiops_labs", - "ml:changePointDetections", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.aiops_labs.ml:changePointDetections", "sideNavStatus": "visible", "title": "Deeplink ml:changePointDetections", }, @@ -614,12 +457,8 @@ Array [ "deepLink": undefined, "href": undefined, "id": "aiops_labs", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - "aiops_labs", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml.aiops_labs", "renderAs": "accordion", "sideNavStatus": "visible", "title": "AIOps labs", @@ -629,17 +468,13 @@ Array [ "href": undefined, "icon": "machineLearningApp", "id": "rootNav:ml", - "isActive": false, - "isGroup": true, - "path": Array [ - "rootNav:ml", - ], + "isElasticInternalLink": false, + "path": "rootNav:ml", "sideNavStatus": "visible", "title": "Machine Learning", "type": "navGroup", }, Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/dev_tools", @@ -647,14 +482,11 @@ Array [ "title": "Deeplink dev_tools", "url": "/mocked/dev_tools", }, - "href": undefined, + "href": "/mocked/dev_tools", "icon": "editorCodeBlock", "id": "devTools", - "isActive": false, - "isGroup": false, - "path": Array [ - "devTools", - ], + "isElasticInternalLink": false, + "path": "devTools", "sideNavStatus": "visible", "title": "Developer tools", "type": "navItem", @@ -663,7 +495,6 @@ Array [ "breadcrumbStatus": "hidden", "children": Array [ Object { - "children": undefined, "deepLink": Object { "baseUrl": "/mocked", "href": "http://mocked/management", @@ -671,42 +502,28 @@ Array [ "title": "Deeplink management", "url": "/mocked/management", }, - "href": undefined, + "href": "/mocked/management", "id": "management", - "isActive": false, - "isGroup": false, - "path": Array [ - "project_settings_project_nav", - "management", - ], + "isElasticInternalLink": false, + "path": "project_settings_project_nav.management", "sideNavStatus": "visible", "title": "Management", }, Object { - "children": undefined, "deepLink": undefined, "href": "https://cloud.elastic.co/deployments/123456789/security/users", "id": "cloudLinkUserAndRoles", - "isActive": false, - "isGroup": false, - "path": Array [ - "project_settings_project_nav", - "cloudLinkUserAndRoles", - ], + "isElasticInternalLink": true, + "path": "project_settings_project_nav.cloudLinkUserAndRoles", "sideNavStatus": "visible", "title": "Mock Users & Roles", }, Object { - "children": undefined, "deepLink": undefined, "href": "https://cloud.elastic.co/account/billing", "id": "cloudLinkBilling", - "isActive": false, - "isGroup": false, - "path": Array [ - "project_settings_project_nav", - "cloudLinkBilling", - ], + "isElasticInternalLink": true, + "path": "project_settings_project_nav.cloudLinkBilling", "sideNavStatus": "visible", "title": "Mock Billing & Subscriptions", }, @@ -715,11 +532,8 @@ Array [ "href": undefined, "icon": "gear", "id": "project_settings_project_nav", - "isActive": false, - "isGroup": true, - "path": Array [ - "project_settings_project_nav", - ], + "isElasticInternalLink": false, + "path": "project_settings_project_nav", "sideNavStatus": "visible", "title": "Project settings", "type": "navGroup", diff --git a/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx index a0c35ace442e4..5a82d4ccd509b 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx @@ -8,36 +8,35 @@ import './setup_jest_mocks'; import React from 'react'; import { type RenderResult, act } from '@testing-library/react'; -import { type Observable, of, BehaviorSubject } from 'rxjs'; +import { of, BehaviorSubject } from 'rxjs'; import type { - ChromeNavLink, ChromeProjectNavigation, ChromeProjectNavigationNode, } from '@kbn/core-chrome-browser'; import { Navigation } from '../src/ui/components/navigation'; import type { RootNavigationItemDefinition } from '../src/ui/types'; - +import { NavigationServices } from '../types'; import { renderNavigation, errorHandler, TestType } from './utils'; describe('Active node', () => { test('should set the active node', async () => { - const navLinks$: Observable = of([ - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + item1: { id: 'item1', title: 'Item 1', baseUrl: '', url: '', href: '', }, - { + item2: { id: 'item2', title: 'Item 2', baseUrl: '', url: '', href: '', }, - ]); + }); let activeNodes$: BehaviorSubject; @@ -47,12 +46,12 @@ describe('Active node', () => { { id: 'group1', title: 'Group 1', - path: ['group1'], + path: 'group1', }, { id: 'item1', title: 'Item 1', - path: ['group1', 'item1'], + path: 'group1.item1', }, ], ]); @@ -75,12 +74,12 @@ describe('Active node', () => { { id: 'group1', title: 'Group 1', - path: ['group1'], + path: 'group1', }, { id: 'item2', title: 'Item 2', - path: ['group1', 'item2'], + path: 'group1.item2', }, ], ]); @@ -112,7 +111,7 @@ describe('Active node', () => { const renderResult = renderNavigation({ navTreeDef: { body: navigationBody }, - services: { navLinks$, activeNodes$: getActiveNodes$() }, + services: { deepLinks$, activeNodes$: getActiveNodes$() }, }); await runTests('treeDef', renderResult); @@ -131,7 +130,7 @@ describe('Active node', () => { ), - services: { navLinks$, activeNodes$: getActiveNodes$() }, + services: { deepLinks$, activeNodes$: getActiveNodes$() }, }); await runTests('uiComponents', renderResult); @@ -139,15 +138,15 @@ describe('Active node', () => { }); test('should override the URL location to set the active node', async () => { - const navLinks$: Observable = of([ - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + item1: { id: 'item1', title: 'Item 1', baseUrl: '', url: '', href: '', }, - ]); + }); let activeNodes$: BehaviorSubject; @@ -197,7 +196,7 @@ describe('Active node', () => { const renderResult = renderNavigation({ navTreeDef: { body: navigationBody }, - services: { navLinks$, activeNodes$: getActiveNodes$() }, + services: { deepLinks$, activeNodes$: getActiveNodes$() }, onProjectNavigationChange, }); @@ -223,7 +222,7 @@ describe('Active node', () => { ), onProjectNavigationChange, - services: { navLinks$, activeNodes$: getActiveNodes$() }, + services: { deepLinks$, activeNodes$: getActiveNodes$() }, }); await runTests('uiComponents', renderResult); diff --git a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx index df8246df69d5e..653af835b6c95 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx @@ -8,13 +8,12 @@ import './setup_jest_mocks'; import React from 'react'; import { type RenderResult } from '@testing-library/react'; -import { type Observable, of } from 'rxjs'; +import { of } from 'rxjs'; import type { ChromeNavLink } from '@kbn/core-chrome-browser'; import { navLinksMock } from '../mocks/src/navlinks'; import { Navigation } from '../src/ui/components/navigation'; import type { RootNavigationItemDefinition } from '../src/ui/types'; - import { getMockFn, renderNavigation, @@ -23,6 +22,7 @@ import { type ProjectNavigationChangeListener, } from './utils'; import { getServicesMock } from '../mocks/src/jest'; +import { NavigationServices } from '../types'; const { cloudLinks: mockCloudLinks } = getServicesMock(); @@ -110,65 +110,42 @@ describe('builds navigation tree', () => { Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.item1", "sideNavStatus": "visible", "title": "Item 1", }, Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item2", - ], + "isElasticInternalLink": false, + "path": "group1.item2", "sideNavStatus": "visible", "title": "Item 2", }, Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.item1", "sideNavStatus": "visible", "title": "Group 1A Item 1", }, Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "group1A_1", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.group1A_1.item1", "sideNavStatus": "visible", "title": "Group 1A_1 Item 1", }, @@ -176,38 +153,28 @@ describe('builds navigation tree', () => { "deepLink": undefined, "href": undefined, "id": "group1A_1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - "group1A_1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.group1A_1", "sideNavStatus": "visible", "title": "Group1A_1", }, ], "deepLink": undefined, + "defaultIsCollapsed": false, "href": undefined, "id": "group1A", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - ], + "isElasticInternalLink": false, + "path": "group1.group1A", "sideNavStatus": "visible", "title": "Group1A", }, ], "deepLink": undefined, + "defaultIsCollapsed": false, "href": undefined, "id": "group1", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - ], + "isElasticInternalLink": false, + "path": "group1", "sideNavStatus": "visible", "title": "", "type": "navGroup", @@ -246,65 +213,42 @@ describe('builds navigation tree', () => { Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.item1", "sideNavStatus": "visible", "title": "Item 1", }, Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item2", - ], + "isElasticInternalLink": false, + "path": "group1.item2", "sideNavStatus": "visible", "title": "Item 2", }, Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.item1", "sideNavStatus": "visible", "title": "Group 1A Item 1", }, Object { "children": Array [ Object { - "children": undefined, "deepLink": undefined, "href": "https://foo", "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "group1A_1", - "item1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.group1A_1.item1", "sideNavStatus": "visible", "title": "Group 1A_1 Item 1", }, @@ -312,13 +256,8 @@ describe('builds navigation tree', () => { "deepLink": undefined, "href": undefined, "id": "group1A_1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - "group1A_1", - ], + "isElasticInternalLink": false, + "path": "group1.group1A.group1A_1", "sideNavStatus": "visible", "title": "Group1A_1", }, @@ -326,24 +265,18 @@ describe('builds navigation tree', () => { "deepLink": undefined, "href": undefined, "id": "group1A", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - ], + "isElasticInternalLink": false, + "path": "group1.group1A", "sideNavStatus": "visible", "title": "Group1A", }, ], "deepLink": undefined, + "defaultIsCollapsed": false, "href": undefined, "id": "group1", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - ], + "isElasticInternalLink": false, + "path": "group1", "sideNavStatus": "visible", "title": "", }, @@ -353,16 +286,19 @@ describe('builds navigation tree', () => { }); test('should read the title from deeplink, prop or React children', async () => { - const navLinks$: Observable = of([ - ...navLinksMock, - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + ...navLinksMock.reduce>((acc, navLink) => { + acc[navLink.id] = navLink; + return acc; + }, {}), + item1: { id: 'item1', title: 'Title from deeplink', baseUrl: '', url: '', href: '', }, - ]); + }); const onProjectNavigationChange = getMockFn(); @@ -425,7 +361,7 @@ describe('builds navigation tree', () => { const renderResult = renderNavigation({ navTreeDef: { body: navigationBody }, - services: { navLinks$ }, + services: { deepLinks$ }, onProjectNavigationChange, }); @@ -454,7 +390,7 @@ describe('builds navigation tree', () => { ), - services: { navLinks$ }, + services: { deepLinks$ }, onProjectNavigationChange, }); @@ -466,15 +402,15 @@ describe('builds navigation tree', () => { }); test('should not render the group if it does not have children AND no href or deeplink', async () => { - const navLinks$: Observable = of([ - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + item1: { id: 'item1', title: 'Title from deeplink', baseUrl: '', url: '', href: '', }, - ]); + }); const onProjectNavigationChange = getMockFn(); const runTests = (type: TestType, { queryByTestId }: RenderResult) => { @@ -523,7 +459,7 @@ describe('builds navigation tree', () => { const renderResult = renderNavigation({ navTreeDef: { body: navigationBody }, - services: { navLinks$ }, + services: { deepLinks$ }, onProjectNavigationChange, }); @@ -548,7 +484,7 @@ describe('builds navigation tree', () => { ), - services: { navLinks$ }, + services: { deepLinks$ }, onProjectNavigationChange, }); @@ -663,11 +599,7 @@ describe('builds navigation tree', () => { const renderResult = renderNavigation({ navigationElement: ( - - - - - + ), services: { recentlyAccessed$ }, @@ -688,28 +620,28 @@ describe('builds navigation tree', () => { { const userAndRolesLink = await findByTestId(/nav-item-group1.cloudLink1/); - expect(userAndRolesLink.textContent).toBe('Mock Users & RolesExternal link'); + expect(userAndRolesLink.textContent).toBe('Mock Users & Roles'); const href = userAndRolesLink.getAttribute('href'); expect(href).toBe(stripLastChar(mockCloudLinks.userAndRoles?.href)); } { const performanceLink = await findByTestId(/nav-item-group1.cloudLink2/); - expect(performanceLink.textContent).toBe('Mock PerformanceExternal link'); + expect(performanceLink.textContent).toBe('Mock Performance'); const href = performanceLink.getAttribute('href'); expect(href).toBe(stripLastChar(mockCloudLinks.performance?.href)); } { const billingLink = await findByTestId(/nav-item-group1.cloudLink3/); - expect(billingLink.textContent).toBe('Mock Billing & SubscriptionsExternal link'); + expect(billingLink.textContent).toBe('Mock Billing & Subscriptions'); const href = billingLink.getAttribute('href'); expect(href).toBe(stripLastChar(mockCloudLinks.billingAndSub?.href)); } { const deploymentLink = await findByTestId(/nav-item-group1.cloudLink4/); - expect(deploymentLink.textContent).toBe('Mock DeploymentExternal link'); + expect(deploymentLink.textContent).toBe('Mock Deployment'); const href = deploymentLink.getAttribute('href'); expect(href).toBe(stripLastChar(mockCloudLinks.deployment?.href)); } diff --git a/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx index 56da3d4494c89..c52f56e3075d4 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx @@ -8,12 +8,11 @@ import './setup_jest_mocks'; import React from 'react'; import { type RenderResult } from '@testing-library/react'; -import { type Observable, of } from 'rxjs'; -import type { ChromeNavLink } from '@kbn/core-chrome-browser'; +import { of } from 'rxjs'; import { Navigation } from '../src/ui/components/navigation'; import type { RootNavigationItemDefinition } from '../src/ui/types'; - +import { NavigationServices } from '../types'; import { getMockFn, renderNavigation, @@ -27,15 +26,15 @@ describe('Links', () => { const onProjectNavigationChange = getMockFn(); const unknownLinkId = 'unknown'; - const navLinks$: Observable = of([ - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + item1: { id: 'item1', title: 'Title from deeplink', baseUrl: '', url: '', href: '', }, - ]); + }); const runTests = async (type: TestType, { findByTestId, queryByTestId }: RenderResult) => { try { @@ -84,7 +83,7 @@ describe('Links', () => { const renderResult = renderNavigation({ navTreeDef: { body: navigationBody }, onProjectNavigationChange, - services: { navLinks$ }, + services: { deepLinks$ }, }); await runTests('treeDef', renderResult); @@ -108,7 +107,7 @@ describe('Links', () => { ), onProjectNavigationChange, - services: { navLinks$ }, + services: { deepLinks$ }, }); await runTests('uiComponents', renderResult); diff --git a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx index 40641eb31d2d2..e588ed384c112 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx @@ -13,7 +13,7 @@ import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import { Navigation } from '../src/ui/components/navigation'; import type { RootNavigationItemDefinition } from '../src/ui/types'; - +import { PanelContentProvider } from '../src/ui'; import { renderNavigation, errorHandler, @@ -21,7 +21,6 @@ import { getMockFn, ProjectNavigationChangeListener, } from './utils'; -import { PanelContentProvider } from '../src/ui'; describe('Panel', () => { test('should render group as panel opener', async () => { @@ -187,7 +186,7 @@ describe('Panel', () => { const [path0 = []] = activeNodes; return (
-

{selectedNode.id}

+

{selectedNode.path}

    {path0.map((node) => (
  • {node.id}
  • @@ -207,12 +206,12 @@ describe('Panel', () => { { id: 'activeGroup1', title: 'Group 1', - path: ['activeGroup1'], + path: 'activeGroup1', }, { id: 'activeItem1', title: 'Item 1', - path: ['activeGroup1', 'activeItem1'], + path: 'activeGroup1.activeItem1', }, ], ]); diff --git a/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx index 7b47c466b838b..a707d1e84b192 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx @@ -6,12 +6,12 @@ * Side Public License, v 1. */ import './setup_jest_mocks'; -import { type Observable, of } from 'rxjs'; +import { of } from 'rxjs'; import type { ChromeNavLink } from '@kbn/core-chrome-browser'; import { navLinksMock } from '../mocks/src/navlinks'; +import { NavigationServices } from '../types'; import type { ProjectNavigationTreeDefinition } from '../src/ui/types'; - import { getMockFn, renderNavigation, type ProjectNavigationChangeListener } from './utils'; describe('Default navigation', () => { @@ -22,16 +22,19 @@ describe('Default navigation', () => { */ test('builds the full navigation tree when only the project is provided', async () => { const onProjectNavigationChange = getMockFn(); - const navLinks$: Observable = of([ - ...navLinksMock, - { + const deepLinks$: NavigationServices['deepLinks$'] = of({ + ...navLinksMock.reduce>((acc, navLink) => { + acc[navLink.id] = navLink; + return acc; + }, {}), + item2: { id: 'item2', title: 'Title from deeplink!', baseUrl: '', url: '', href: '', }, - ]); + }); const projectNavigationTree: ProjectNavigationTreeDefinition = [ { @@ -62,7 +65,7 @@ describe('Default navigation', () => { renderNavigation({ projectNavigationTree, onProjectNavigationChange, - services: { navLinks$ }, + services: { deepLinks$ }, }); expect(onProjectNavigationChange).toHaveBeenCalled(); diff --git a/packages/shared-ux/chrome/navigation/kibana.jsonc b/packages/shared-ux/chrome/navigation/kibana.jsonc index 74cd1ccea3252..60bfed4d5796f 100644 --- a/packages/shared-ux/chrome/navigation/kibana.jsonc +++ b/packages/shared-ux/chrome/navigation/kibana.jsonc @@ -1,5 +1,5 @@ { - "type": "shared-common", + "type": "shared-browser", "id": "@kbn/shared-ux-chrome-navigation", "owner": "@elastic/appex-sharedux" } diff --git a/packages/shared-ux/chrome/navigation/mocks/src/jest.ts b/packages/shared-ux/chrome/navigation/mocks/src/jest.ts index 9ae6c2b2a6952..750b3faada7b1 100644 --- a/packages/shared-ux/chrome/navigation/mocks/src/jest.ts +++ b/packages/shared-ux/chrome/navigation/mocks/src/jest.ts @@ -13,18 +13,25 @@ import { navLinksMock } from './navlinks'; const activeNodes: ChromeProjectNavigationNode[][] = []; +const defaultDeepLinks = { + ...navLinksMock.reduce>((acc, navLink) => { + acc[navLink.id] = navLink; + return acc; + }, {}), +}; + export const getServicesMock = ({ - navLinks = navLinksMock, -}: { navLinks?: ChromeNavLink[] } = {}): NavigationServices => { + deepLinks = defaultDeepLinks, +}: { deepLinks?: Readonly> } = {}): NavigationServices => { const navigateToUrl = jest.fn().mockResolvedValue(undefined); const basePath = { prepend: jest.fn((path: string) => `/base${path}`) }; const recentlyAccessed$ = new BehaviorSubject([]); - const navLinks$ = new BehaviorSubject(navLinks); + const deepLinks$ = new BehaviorSubject(deepLinks); return { basePath, recentlyAccessed$, - navLinks$, + deepLinks$, navIsOpen: true, navigateToUrl, onProjectNavigationChange: jest.fn(), diff --git a/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts b/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts index df1416ec0f793..7a7d0e5fbe3ea 100644 --- a/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts +++ b/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts @@ -14,7 +14,7 @@ import { NavigationServices } from '../../types'; type Arguments = NavigationServices; export type Params = Pick< Arguments, - 'navIsOpen' | 'recentlyAccessed$' | 'activeNodes$' | 'navLinks$' | 'onProjectNavigationChange' + 'navIsOpen' | 'recentlyAccessed$' | 'activeNodes$' | 'deepLinks$' | 'onProjectNavigationChange' >; export class StorybookMock extends AbstractStorybookMock<{}, NavigationServices> { @@ -41,7 +41,7 @@ export class StorybookMock extends AbstractStorybookMock<{}, NavigationServices> basePath: { prepend: (suffix: string) => `/basepath${suffix}` }, navigateToUrl, recentlyAccessed$: params.recentlyAccessed$ ?? new BehaviorSubject([]), - navLinks$: params.navLinks$ ?? new BehaviorSubject([]), + deepLinks$: params.deepLinks$ ?? new BehaviorSubject({}), onProjectNavigationChange: params.onProjectNavigationChange ?? (() => undefined), activeNodes$: params.activeNodes$ ?? new BehaviorSubject([]), isSideNavCollapsed: true, diff --git a/packages/shared-ux/chrome/navigation/src/cloud_links.tsx b/packages/shared-ux/chrome/navigation/src/cloud_links.tsx index ce9d9e5990aab..b900daa340b44 100644 --- a/packages/shared-ux/chrome/navigation/src/cloud_links.tsx +++ b/packages/shared-ux/chrome/navigation/src/cloud_links.tsx @@ -7,7 +7,6 @@ */ import { i18n } from '@kbn/i18n'; import type { CloudLinkId } from '@kbn/core-chrome-browser'; -import type { CloudStart } from '@kbn/cloud-plugin/public'; export interface CloudLink { title: string; @@ -18,7 +17,12 @@ export type CloudLinks = { [id in CloudLinkId]?: CloudLink; }; -export const getCloudLinks = (cloud: CloudStart): CloudLinks => { +export const getCloudLinks = (cloud: { + billingUrl?: string; + deploymentUrl?: string; + performanceUrl?: string; + usersAndRolesUrl?: string; +}): CloudLinks => { const { billingUrl, deploymentUrl, performanceUrl, usersAndRolesUrl } = cloud; const links: CloudLinks = {}; diff --git a/packages/shared-ux/chrome/navigation/src/navnode_utils.ts b/packages/shared-ux/chrome/navigation/src/navnode_utils.ts new file mode 100644 index 0000000000000..561950959d082 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/src/navnode_utils.ts @@ -0,0 +1,173 @@ +/* + * Copyright 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 { + AppDeepLinkId, + ChromeNavLink, + ChromeProjectNavigationNode, + CloudLinkId, + SideNavNodeStatus, +} from '@kbn/core-chrome-browser'; + +import type { CloudLinks } from './cloud_links'; +import type { NodeProps } from './ui/types'; +import { getNavigationNodeHref, getNavigationNodeId, isAbsoluteLink } from './utils'; + +/** + * We don't have currently a way to know if a user has access to a Cloud section. + * TODO: This function will have to be revisited once we have an API from Cloud to know the user + * permissions. + */ +function hasUserAccessToCloudLink(): boolean { + return true; +} + +function getNodeStatus( + { + link, + deepLink, + cloudLink, + sideNavStatus, + }: { + link?: string; + deepLink?: ChromeNavLink; + cloudLink?: CloudLinkId; + sideNavStatus?: SideNavNodeStatus; + }, + { cloudLinks }: { cloudLinks: CloudLinks } +): SideNavNodeStatus | 'remove' { + if (link && !deepLink) { + // If a link is provided, but no deepLink is found, don't render anything + return 'remove'; + } + + if (cloudLink) { + if (!cloudLinks[cloudLink]) { + // Invalid cloudLinkId or link url has not been set in kibana.yml + return 'remove'; + } + if (!hasUserAccessToCloudLink()) return 'remove'; + } + + if (deepLink && deepLink.hidden) return 'hidden'; + + return sideNavStatus ?? 'visible'; +} + +function getTitleForNode< + LinkId extends AppDeepLinkId = AppDeepLinkId, + Id extends string = string, + ChildrenId extends string = Id +>( + navNode: NodeProps, + { deepLink, cloudLinks }: { deepLink?: ChromeNavLink; cloudLinks: CloudLinks } +): string { + const { children } = navNode; + if (navNode.title) { + return navNode.title; + } + + if (typeof children === 'string') { + return children; + } + + if (deepLink?.title) { + return deepLink.title; + } + + if (navNode.cloudLink) { + return cloudLinks[navNode.cloudLink]?.title ?? ''; + } + + return ''; +} + +function validateNodeProps< + LinkId extends AppDeepLinkId = AppDeepLinkId, + Id extends string = string, + ChildrenId extends string = Id +>({ id, link, href, cloudLink, renderAs }: NodeProps) { + if (link && cloudLink) { + throw new Error( + `[Chrome navigation] Error in node [${id}]. Only one of "link" or "cloudLink" can be provided.` + ); + } + if (href && cloudLink) { + throw new Error( + `[Chrome navigation] Error in node [${id}]. Only one of "href" or "cloudLink" can be provided.` + ); + } + if (renderAs === 'panelOpener' && !link) { + throw new Error( + `[Chrome navigation] Error in node [${id}]. If renderAs is set to "panelOpener", a "link" must also be provided.` + ); + } + if (renderAs === 'item' && !link) { + throw new Error( + `[Chrome navigation] Error in node [${id}]. If renderAs is set to "item", a "link" must also be provided.` + ); + } +} + +export const initNavNode = < + LinkId extends AppDeepLinkId = AppDeepLinkId, + Id extends string = string, + ChildrenId extends string = Id +>( + node: NodeProps, + { cloudLinks, deepLinks }: { cloudLinks: CloudLinks; deepLinks: Record } +): ChromeProjectNavigationNode | null => { + validateNodeProps(node); + + const { + cloudLink, + link, + parentNodePath, + rootIndex = 0, + treeDepth = 0, + index = 0, + children, + ...navNodeFromProps + } = node; + const deepLink = link !== undefined ? deepLinks[link] : undefined; + const sideNavStatus = getNodeStatus( + { + link, + deepLink, + cloudLink, + sideNavStatus: navNodeFromProps.sideNavStatus, + }, + { cloudLinks } + ); + if (sideNavStatus === 'remove') { + return null; + } + + const id = getNavigationNodeId(node, () => `node-${rootIndex}-${treeDepth}-${index}`) as Id; + const title = getTitleForNode(node, { deepLink, cloudLinks }); + const isElasticInternalLink = cloudLink != null; + const href = isElasticInternalLink ? cloudLinks[cloudLink]?.href : node.href; + const path = parentNodePath ? `${parentNodePath}.${id}` : id; + + if (href && !isAbsoluteLink(href)) { + throw new Error(`href must be an absolute URL. Node id [${id}].`); + } + + const navNode: ChromeProjectNavigationNode = { + ...navNodeFromProps, + id, + href: getNavigationNodeHref({ href, deepLink }), + path, + title, + deepLink, + isElasticInternalLink, + sideNavStatus, + }; + + return navNode; +}; diff --git a/packages/shared-ux/chrome/navigation/src/services.tsx b/packages/shared-ux/chrome/navigation/src/services.tsx index f77a288160a3d..54e45ede100e4 100644 --- a/packages/shared-ux/chrome/navigation/src/services.tsx +++ b/packages/shared-ux/chrome/navigation/src/services.tsx @@ -8,6 +8,9 @@ import React, { FC, useContext, useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs'; +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; + import { NavigationKibanaDependencies, NavigationServices } from '../types'; import { CloudLink, CloudLinks, getCloudLinks } from './cloud_links'; @@ -61,17 +64,31 @@ export const NavigationKibanaProvider: FC = ({ const { chrome, http } = core; const { basePath } = http; const { navigateToUrl } = core.application; + const { billingUrl, deploymentUrl, performanceUrl, usersAndRolesUrl } = cloud; - const cloudLinks: CloudLinks = useMemo( - () => (cloud ? parseCloudURLs(getCloudLinks(cloud)) : {}), - [cloud] - ); + const cloudLinks: CloudLinks = useMemo(() => { + return parseCloudURLs( + getCloudLinks({ billingUrl, deploymentUrl, performanceUrl, usersAndRolesUrl }) + ); + }, [billingUrl, deploymentUrl, performanceUrl, usersAndRolesUrl]); const isSideNavCollapsed = useObservable(chrome.getIsSideNavCollapsed$(), true); + const navLinks$ = useMemo(() => chrome.navLinks.getNavLinks$(), [chrome.navLinks]); + const deepLinks$ = useMemo(() => { + return navLinks$.pipe( + map((navLinks) => { + return navLinks.reduce((acc, navLink) => { + acc[navLink.id] = navLink; + return acc; + }, {} as Record); + }) + ); + }, [navLinks$]); + const value: NavigationServices = { basePath, recentlyAccessed$: chrome.recentlyAccessed.get$(), - navLinks$: chrome.navLinks.getNavLinks$(), + deepLinks$, navigateToUrl, navIsOpen: true, onProjectNavigationChange: serverless.setNavigation, diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx index 8f74abee6a110..d56495b810f4a 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx @@ -8,19 +8,18 @@ import React, { createContext, - useState, useCallback, ReactNode, useMemo, - useEffect, useContext, useRef, + Children, } from 'react'; import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; -import useDebounce from 'react-use/lib/useDebounce'; import useObservable from 'react-use/lib/useObservable'; import { useNavigation as useNavigationServices } from '../../services'; +import { getChildType } from '../../utils'; import { RegisterFunction, UnRegisterFunction } from '../types'; import { NavigationFooter } from './navigation_footer'; import { NavigationGroup } from './navigation_group'; @@ -31,17 +30,12 @@ import { PanelProvider, type ContentProvider } from './panel'; interface Context { register: RegisterFunction; - updateFooterChildren: (children: ReactNode) => void; unstyled: boolean; activeNodes: ChromeProjectNavigationNode[][]; } const NavigationContext = createContext({ - register: () => ({ - unregister: () => {}, - path: [], - }), - updateFooterChildren: () => {}, + register: () => () => {}, unstyled: false, activeNodes: [], }); @@ -77,83 +71,105 @@ export function Navigation({ const idx = useRef(0); const activeNodes = useObservable(activeNodes$, []); - const [navigationItems, setNavigationItems] = useState< - Record - >({}); - const [debouncedNavigationItems, setDebouncedNavigationItems] = useState< - Record - >({}); - const [footerChildren, setFooterChildren] = useState(null); - - const unregister: UnRegisterFunction = useCallback((id: string) => { - setNavigationItems((prevItems) => { - const updatedItems = { ...prevItems }; - delete updatedItems[id]; - return updatedItems; + const navigationItemsRef = useRef>({}); + + const onNavigationItemsChange = useCallback(() => { + const navigationTree = Object.values(navigationItemsRef.current).sort((a, b) => { + const aOrder = orderChildrenRef.current[a.id]; + const bOrder = orderChildrenRef.current[b.id]; + return aOrder - bOrder; }); - }, []); + + // This will update the navigation tree in the Chrome service (calling the serverless.setNavigation()) + onProjectNavigationChange({ navigationTree }); + }, [onProjectNavigationChange]); + + const unregister = useCallback( + (id: string) => { + const updatedItems = { ...navigationItemsRef.current }; + delete updatedItems[id]; + navigationItemsRef.current = updatedItems; + + onNavigationItemsChange(); + }, + [onNavigationItemsChange] + ); const register = useCallback( - (navNode) => { + (navNode, order): UnRegisterFunction => { if (orderChildrenRef.current[navNode.id] === undefined) { - orderChildrenRef.current[navNode.id] = idx.current++; + orderChildrenRef.current[navNode.id] = order ?? idx.current++; } - setNavigationItems((prevItems) => { - return { - ...prevItems, - [navNode.id]: navNode, - }; - }); + const updatedRef = { ...navigationItemsRef.current, [navNode.id]: navNode }; + navigationItemsRef.current = updatedRef; + + onNavigationItemsChange(); - return { - unregister, - path: [navNode.id], - }; + return () => unregister(navNode.id); }, - [unregister] + [unregister, onNavigationItemsChange] ); const contextValue = useMemo( () => ({ register, - updateFooterChildren: setFooterChildren, unstyled, activeNodes, }), [register, unstyled, activeNodes] ); - useDebounce( - () => { - setDebouncedNavigationItems(navigationItems); - }, - 100, - [navigationItems] - ); + const childrenParsed = useMemo(() => { + let footerChildren: ReactNode; + let rootIndex = 0; + + const parseChildren = (_children: ReactNode, wrapperComponent = '') => { + const parsed: ReactNode[] = []; + Children.forEach(_children, (child, i) => { + if (!React.isValidElement(child)) { + return; + } + + const childType = getChildType(child); + if (childType === 'unknown' && unstyled === false) { + throw new Error( + `${wrapperComponent} only accepts , and as children. Received ${child.type}` + ); + } + + if (childType === 'footer') { + if (footerChildren) { + throw new Error('Only one is allowed'); + } + footerChildren = parseChildren(child.props.children, ''); + return; + } + + // We add a "rootIndex" prop to each child to keep track of the order of the children + // and correctly set the order of nodes in the navigation tree independently of when a + // node register itself (it could be after a deepLink is being activated). + parsed.push({ ...child, props: { ...child.props, rootIndex } }); + rootIndex += 1; + }); - useEffect(() => { - const navigationTree = Object.values(debouncedNavigationItems).sort((a, b) => { - const aOrder = orderChildrenRef.current[a.id]; - const bOrder = orderChildrenRef.current[b.id]; - return aOrder - bOrder; - }); + return parsed; + }; - // This will update the navigation tree in the Chrome service (calling the serverless.setNavigation()) - onProjectNavigationChange({ - navigationTree, - }); - }, [debouncedNavigationItems, onProjectNavigationChange]); + const bodyChildren: ReactNode[] = parseChildren(children); + + return { body: bodyChildren, footer: footerChildren }; + }, [children, unstyled]); return ( - {children} + {childrenParsed.body} diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_footer.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_footer.tsx index d4b78c1e93053..083e8e66ee55a 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_footer.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_footer.tsx @@ -6,23 +6,14 @@ * Side Public License, v 1. */ -import React, { useEffect } from 'react'; -import { useNavigation } from './navigation'; +import { type FC } from 'react'; export interface Props { - children?: React.ReactNode; + children?: JSX.Element[]; } -function NavigationFooterComp({ children }: Props) { - const { updateFooterChildren } = useNavigation(); - - useEffect(() => { - if (children) { - updateFooterChildren(children); - } - }, [children, updateFooterChildren]); - +// Note: this component is only used to detect which children are part of the body and which +// are part of the footer. See the "childrenParsed" value of the component. +export const NavigationFooter: FC = () => { return null; -} - -export const NavigationFooter = React.memo(NavigationFooterComp); +}; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_group.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_group.tsx index 2f3ecbd381b45..11beb966963a1 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_group.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_group.tsx @@ -6,31 +6,98 @@ * Side Public License, v 1. */ -import React, { createContext, useCallback, useMemo, useContext } from 'react'; -import type { AppDeepLinkId, ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; +import React, { useMemo, Children, ReactNode, useEffect, useRef } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import deepEqual from 'react-fast-compare'; +import type { + AppDeepLinkId, + ChromeProjectNavigationNode, + ChromeNavLink, +} from '@kbn/core-chrome-browser'; import { useNavigation as useNavigationServices } from '../../services'; -import { useInitNavNode } from '../hooks'; -import type { NodeProps, NodePropsEnhanced, RegisterFunction } from '../types'; +import type { NodeProps } from '../types'; import { NavigationSectionUI } from './navigation_section_ui'; import { useNavigation } from './navigation'; import { NavigationBucket, type Props as NavigationBucketProps } from './navigation_bucket'; +import { generateUniqueNodeId, getChildType } from '../../utils'; +import { initNavNode } from '../../navnode_utils'; +import { CloudLinks } from '../../cloud_links'; + +/** + * Handler to convert the JSX children of the NavigationGroup to the ChromeProjectNavigationNode + * interface. We do that by parsing the children with the React.Children func, read their prop + * and initiate the node objects. + */ +const jsxChildrenToNavigationNode = ( + { + parentNodePath, + jsxChildren, + rootIndex, + treeDepth, + }: { parentNodePath: string; jsxChildren?: ReactNode; rootIndex: number; treeDepth: number }, + { cloudLinks, deepLinks }: { cloudLinks: CloudLinks; deepLinks: Record } +): ChromeProjectNavigationNode[] | undefined => { + if (!jsxChildren) return undefined; + + const navigationNodes: ChromeProjectNavigationNode[] = []; + + Children.forEach(jsxChildren, (child, index) => { + if (!React.isValidElement(child)) { + return; + } + const title = + typeof child.props.children === 'string' ? child.props.children : child.props.title; + const childNode = initNavNode( + { ...child.props, title, rootIndex, treeDepth, index, parentNodePath }, + { cloudLinks, deepLinks } + ); -interface Context { - register: RegisterFunction; -} + if (!childNode) return; + + const childType = getChildType(child); + + if (childType !== 'group') { + if (childType === 'item') { + if (child.props.children && typeof child.props.children !== 'string') { + // Render the node item + childNode.renderItem = () => child.props.children; + } + navigationNodes.push(childNode); + } else { + // This is a custom JSX node, render it "as is" in the nav. + navigationNodes.push({ + id: generateUniqueNodeId(), + title: '', + path: '', + renderItem: () => child, + }); + } + return; + } -export const NavigationGroupContext = createContext(undefined); + if (child.props?.children) { + navigationNodes.push({ + ...childNode, + // Recursively add all the children of the group + children: jsxChildrenToNavigationNode( + { + parentNodePath: childNode.path, + jsxChildren: child.props.children, + rootIndex, + treeDepth: treeDepth + 1, + }, + { cloudLinks, deepLinks } + ), + }); + return; + } -export function useNavigationGroup( - throwIfNotFound: T = true as T -): T extends true ? Context : Context | undefined { - const context = useContext(NavigationGroupContext); - if (!context && throwIfNotFound) { - throw new Error('useNavigationGroup must be used within a NavigationGroup provider'); - } - return context as T extends true ? Context : Context | undefined; -} + navigationNodes.push(childNode); + }); + + return navigationNodes.length > 0 ? navigationNodes : undefined; +}; export interface Props< LinkId extends AppDeepLinkId = AppDeepLinkId, @@ -45,82 +112,60 @@ function NavigationGroupInternalComp< Id extends string = string, ChildrenId extends string = Id >(props: Props) { - const { cloudLinks } = useNavigationServices(); - const navigationContext = useNavigation(); - - const { children, node } = useMemo(() => { - const { children: _children, defaultIsCollapsed, ...rest } = props; - const nodeEnhanced: Omit, 'children'> = { - ...rest, - isActive: defaultIsCollapsed !== undefined ? defaultIsCollapsed === false : undefined, - isGroup: true, - }; - return { - children: _children, - node: nodeEnhanced, - }; - }, [props]); - - const { navNode, registerChildNode, path, childrenNodes } = useInitNavNode(node, { cloudLinks }); + const { cloudLinks, deepLinks$ } = useNavigationServices(); + const { register } = useNavigation(); + const deepLinks = useObservable(deepLinks$, {}); + const { rootIndex = 0 } = props; - // We add to the nav node the children that have mounted and registered themselves. - // Those children render in the UI inside the NavigationSectionUI -> EuiCollapsibleNavItem -> items - const navNodeWithChildren = useMemo(() => { - if (!navNode) return null; + const navNodeRef = useRef(); + const childrenNodesRef = useRef(); - const hasChildren = Object.keys(childrenNodes).length > 0; - const withChildren: ChromeProjectNavigationNode = { - ...navNode, - children: hasChildren ? Object.values(childrenNodes) : undefined, - }; + const navNode = useMemo(() => { + const _navNode = initNavNode(props, { cloudLinks, deepLinks }); - return withChildren; - }, [navNode, childrenNodes]); + if (!_navNode) return null; - const unstyled = props.unstyled ?? navigationContext.unstyled; + const childrenNodes = jsxChildrenToNavigationNode( + { parentNodePath: _navNode.path, jsxChildren: props.children, rootIndex, treeDepth: 1 }, + { cloudLinks, deepLinks } + ); - const renderContent = useCallback(() => { - if (!path || !navNodeWithChildren) { - return null; + const childrenChanged = deepEqual(childrenNodes, childrenNodesRef.current) === false; + if (childrenChanged) { + childrenNodesRef.current = childrenNodes; } - if (navNodeWithChildren.sideNavStatus === 'hidden') return null; + const nextValue = { + ..._navNode, + children: childrenNodesRef.current, + }; - if (unstyled) { - // No UI for unstyled groups - return children; + const hasChanged = deepEqual(nextValue, navNodeRef.current) === false; + if (hasChanged) { + navNodeRef.current = nextValue; } - // We will only render the component for root groups. The nested group - // are handled by the EuiCollapsibleNavItem component through its "items" prop. - const isRootLevel = path && path.length === 1; + if (navNodeRef.current === undefined) { + // Adding this check for TS purpose, it should never be undefined. + throw new Error('Navnode ref is undefined.'); + } - return ( - <> - {isRootLevel && } - {/* We render the children so they mount and can **register** themselves but - visually they don't appear here in the DOM. They are rendered inside the - "items" prop (see ) */} - {children} - - ); - }, [navNodeWithChildren, path, children, unstyled]); + return navNodeRef.current; + }, [props, cloudLinks, deepLinks, rootIndex]); - const contextValue = useMemo(() => { - return { - register: registerChildNode, - }; - }, [registerChildNode]); + /** Register when mounting and whenever the internal nav node changes */ + useEffect(() => { + if (navNode) { + return register(navNode, rootIndex); + } + return undefined; + }, [register, navNode, rootIndex]); - if (!navNode) { + if (!navNode || navNode.sideNavStatus === 'hidden') { return null; } - return ( - - {renderContent()} - - ); + return ; } function NavigationGroupComp< diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item.tsx index dae8cef6f4eee..068f12d7ccd95 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item.tsx @@ -6,16 +6,18 @@ * Side Public License, v 1. */ -import React, { Fragment, useEffect, useMemo } from 'react'; +import React, { Fragment, useEffect, useMemo, useRef } from 'react'; import type { AppDeepLinkId, ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import { EuiCollapsibleNavItem } from '@elastic/eui'; import classNames from 'classnames'; +import deepEqual from 'react-fast-compare'; +import useObservable from 'react-use/lib/useObservable'; import { useNavigation as useNavigationServices } from '../../services'; -import { useInitNavNode } from '../hooks'; -import type { NodeProps, NodePropsEnhanced } from '../types'; +import type { NodeProps } from '../types'; import { useNavigation } from './navigation'; -import { getNavigationNodeHref } from '../../utils'; +import { isActiveFromUrl } from '../../utils'; +import { initNavNode } from '../../navnode_utils'; export interface Props< LinkId extends AppDeepLinkId = AppDeepLinkId, @@ -30,31 +32,55 @@ function NavigationItemComp< Id extends string = string, ChildrenId extends string = Id >(props: Props) { - const { cloudLinks, navigateToUrl } = useNavigationServices(); - const navigationContext = useNavigation(); - const navNodeRef = React.useRef(null); + const { cloudLinks, navigateToUrl, deepLinks$ } = useNavigationServices(); + const { unstyled: unstyledFromContext, register, activeNodes } = useNavigation(); + const deepLinks = useObservable(deepLinks$, {}); + const navNodeRef = useRef(); + const { rootIndex, appendHorizontalRule } = props; const { children, node } = useMemo(() => { const { children: _children, ...rest } = props; - const nodeEnhanced: Omit, 'children'> = { - ...rest, - isGroup: false, - }; + if (typeof _children === 'string') { - nodeEnhanced.title = nodeEnhanced.title ?? _children; + rest.title = rest.title ?? _children; } + return { children: _children, - node: nodeEnhanced, + node: rest, }; }, [props]); - const unstyled = props.unstyled ?? navigationContext.unstyled; + const unstyled = props.unstyled ?? unstyledFromContext; + + const navNode = useMemo(() => { + const _navNode = initNavNode(node, { cloudLinks, deepLinks }); + if (!_navNode) return null; + + const hasChanged = deepEqual(_navNode, navNodeRef.current) === false; + if (hasChanged) { + navNodeRef.current = _navNode; + } + + if (navNodeRef.current === undefined) { + // Adding this check for TS purpose, it should never be undefined. + throw new Error('Navnode ref is undefined.'); + } - const { navNode } = useInitNavNode(node, { cloudLinks }); + return navNodeRef.current; + }, [node, cloudLinks, deepLinks]); + + if (navNode && appendHorizontalRule) { + throw new Error( + `[Chrome navigation] Error in node [${navNode.id}]. "appendHorizontalRule" can only be added for group with children.` + ); + } useEffect(() => { - navNodeRef.current = navNode; - }, [navNode]); + if (navNode) { + return register(navNode, rootIndex); + } + return undefined; + }, [register, navNode, rootIndex]); if (!navNode) { return null; @@ -71,40 +97,34 @@ function NavigationItemComp< return {navNode.title}; } - const isRootLevel = navNode.path.length === 1; - - if (isRootLevel) { - const href = getNavigationNodeHref(navNode); - const dataTestSubj = classNames(`nav-item`, { - [`nav-item-deepLinkId-${navNode.deepLink?.id}`]: !!navNode.deepLink, - [`nav-item-isActive`]: navNode.isActive, - }); - - return ( - { - e.preventDefault(); - e.stopPropagation(); - if (href) { - navigateToUrl(href); - } - }, - }} - /> - ); - } + const isActive = isActiveFromUrl(navNode.path, activeNodes); + + const { href } = navNode; + const dataTestSubj = classNames(`nav-item`, { + [`nav-item-deepLinkId-${navNode.deepLink?.id}`]: !!navNode.deepLink, + [`nav-item-isActive`]: isActive, + }); - // We don't render anything in the UI for non root item as those register themselves on the parent (Group) - // updating its "childrenNodes" state which are then converted to "items" for the EuiCollapsibleNavItem component. - return null; + return ( + { + e.preventDefault(); + e.stopPropagation(); + if (href) { + navigateToUrl(href); + } + }, + }} + /> + ); } export const NavigationItem = React.memo(NavigationItemComp) as typeof NavigationItemComp; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx index eb12759eb09d8..8e4475a979a06 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_item_open_panel.tsx @@ -23,9 +23,9 @@ import { } from '@elastic/eui'; import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import type { NavigateToUrlFn } from '../../../types/internal'; -import { nodePathToString } from '../../utils'; import { useNavigation as useServices } from '../../services'; import { usePanel } from './panel'; +import { isActiveFromUrl } from '../../utils'; const getStyles = (euiTheme: EuiThemeComputed<{}>) => css` * { @@ -47,17 +47,19 @@ const getStyles = (euiTheme: EuiThemeComputed<{}>) => css` interface Props { item: ChromeProjectNavigationNode; navigateToUrl: NavigateToUrlFn; + activeNodes: ChromeProjectNavigationNode[][]; } -export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl }: Props) => { +export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl, activeNodes }: Props) => { const { euiTheme } = useEuiTheme(); const { open: openPanel, close: closePanel, selectedNode } = usePanel(); const { isSideNavCollapsed } = useServices(); - const { title, deepLink, isActive, children } = item; - const id = nodePathToString(item); + const { title, deepLink, children } = item; + const { id, path } = item; const href = deepLink?.url ?? item.href; const isNotMobile = useIsWithinMinBreakpoint('s'); const isIconVisible = isNotMobile && !isSideNavCollapsed && !!children && children.length > 0; + const isActive = isActiveFromUrl(item.path, activeNodes); const itemClassNames = classNames( 'sideNavItem', @@ -65,12 +67,12 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl }: Prop getStyles(euiTheme) ); - const dataTestSubj = classNames(`nav-item`, `nav-item-${id}`, { + const dataTestSubj = classNames(`nav-item`, `nav-item-${path}`, { [`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink, [`nav-item-id-${id}`]: id, [`nav-item-isActive`]: isActive, }); - const buttonDataTestSubj = classNames(`panelOpener`, `panelOpener-${id}`, { + const buttonDataTestSubj = classNames(`panelOpener`, `panelOpener-${path}`, { [`panelOpener-deepLinkId-${deepLink?.id}`]: !!deepLink, }); @@ -113,7 +115,7 @@ export const NavigationItemOpenPanel: FC = ({ item, navigateToUrl }: Prop {isIconVisible && ( Boolean(navNod const itemIsVisible = (item: ChromeProjectNavigationNode) => { if (item.sideNavStatus === 'hidden') return false; + if (item.renderItem) return true; + if (nodeHasLink(item)) { return true; } @@ -61,8 +66,8 @@ const getRenderAs = (navNode: ChromeProjectNavigationNode): RenderAs => { }; const getTestSubj = (navNode: ChromeProjectNavigationNode, isActive = false): string => { - const { id, deepLink } = navNode; - return classnames(`nav-item`, `nav-item-${id}`, { + const { id, path, deepLink } = navNode; + return classnames(`nav-item`, `nav-item-${path}`, { [`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink, [`nav-item-id-${id}`]: id, [`nav-item-isActive`]: isActive, @@ -79,9 +84,7 @@ const filterChildren = ( const serializeNavNode = (navNode: ChromeProjectNavigationNode) => { const serialized: ChromeProjectNavigationNode = { ...navNode, - id: nodePathToString(navNode), children: filterChildren(navNode.children), - href: getNavigationNodeHref(navNode), }; serialized.renderAs = getRenderAs(serialized); @@ -130,7 +133,7 @@ const renderBlockTitle: ( const renderGroup = ( navGroup: ChromeProjectNavigationNode, - groupItems: Array, + groupItems: Array, { spaceBefore = DEFAULT_SPACE_BETWEEN_LEVEL_1_GROUPS }: { spaceBefore?: EuiThemeSize | null } = {} ): Required['items'] => { let itemPrepend: EuiCollapsibleNavItemProps | EuiCollapsibleNavSubItemProps | null = null; @@ -163,31 +166,35 @@ const nodeToEuiCollapsibleNavProps = ( closePanel, isSideNavCollapsed, treeDepth, - itemsState, + itemsAccordionState, + activeNodes, }: { navigateToUrl: NavigateToUrlFn; openPanel: PanelContext['open']; closePanel: PanelContext['close']; isSideNavCollapsed: boolean; treeDepth: number; - itemsState: AccordionItemsState; + itemsAccordionState: AccordionItemsState; + activeNodes: ChromeProjectNavigationNode[][]; } ): { - items: Array; + items: Array; isVisible: boolean; } => { const { navNode, isItem, hasChildren, hasLink } = serializeNavNode(_navNode); + const isActive = isActiveFromUrl(navNode.path, activeNodes); - const { id, title, href, icon, renderAs, isActive, spaceBefore: _spaceBefore } = navNode; - const isExternal = Boolean(href) && isAbsoluteLink(href!); + const { id, path, href, renderAs } = navNode; + const isExternal = Boolean(href) && !navNode.isElasticInternalLink && isAbsoluteLink(href!); const isAccordion = hasChildren && !isItem; - const isAccordionExpanded = (itemsState[id]?.isCollapsed ?? DEFAULT_IS_COLLAPSED) === false; + const isAccordionExpanded = + (itemsAccordionState[path]?.isCollapsed ?? DEFAULT_IS_COLLAPSED) === false; const isSelected = isAccordion && isAccordionExpanded ? false : isActive; const dataTestSubj = getTestSubj(navNode, isSelected); - let spaceBefore = _spaceBefore; + let spaceBefore = navNode.spaceBefore; if (spaceBefore === undefined && treeDepth === 1 && hasChildren) { // For groups at level 1 that don't have a space specified we default to add a "m" // space. For all other groups, unless specified, there is no vertical space. @@ -195,9 +202,15 @@ const nodeToEuiCollapsibleNavProps = ( } if (renderAs === 'panelOpener') { - const items: EuiCollapsibleNavSubItemProps[] = [ + const items: EuiCollapsibleNavSubItemPropsEnhanced[] = [ { - renderItem: () => , + renderItem: () => ( + + ), }, ]; if (spaceBefore) { @@ -227,7 +240,8 @@ const nodeToEuiCollapsibleNavProps = ( closePanel, isSideNavCollapsed, treeDepth: treeDepth + 1, - itemsState, + itemsAccordionState, + activeNodes, }) ) .filter(({ isVisible }) => isVisible) @@ -264,17 +278,30 @@ const nodeToEuiCollapsibleNavProps = ( // Render as an accordion or a link (handled by EUI) depending if // "items" is undefined or not. If it is undefined --> a link, otherwise an // accordion is rendered. - const items: Array = [ + if (navNode.renderItem) { + return { + items: [ + { + renderItem: navNode.renderItem, + }, + ], + isVisible: true, + }; + } + + const items: Array = [ + // @ts-ignore - TODO { id, - title, + path, isSelected, linkProps, onClick, href, + icon: navNode.icon, + title: navNode.title, items: subItems, ['data-test-subj']: dataTestSubj, - icon, iconProps: { size: treeDepth === 0 ? 'm' : 's' }, }, ]; @@ -317,19 +344,22 @@ interface Props { navNode: ChromeProjectNavigationNode; } -export const NavigationSectionUI: FC = ({ navNode }) => { +export const NavigationSectionUI: FC = React.memo(({ navNode: _navNode }) => { + const { activeNodes } = useNavigation(); const { navigateToUrl, isSideNavCollapsed } = useServices(); + + const { navNode } = useMemo(() => serializeNavNode(_navNode), [_navNode]); const { open: openPanel, close: closePanel } = usePanel(); const navNodesById = useMemo(() => { const byId = { - [nodePathToString(navNode)]: navNode, + [navNode.path]: navNode, }; const parse = (navNodes?: ChromeProjectNavigationNode[]) => { if (!navNodes) return; navNodes.forEach((childNode) => { - byId[nodePathToString(childNode)] = childNode; + byId[childNode.path] = childNode; parse(childNode.children); }); }; @@ -338,13 +368,21 @@ export const NavigationSectionUI: FC = ({ navNode }) => { return byId; }, [navNode]); - const [itemsState, setItemsState] = useState(() => { + const [itemsAccordionState, setItemsAccordionState] = useState(() => { return Object.entries(navNodesById).reduce((acc, [_id, node]) => { if (node.children) { + let isCollapsed = DEFAULT_IS_COLLAPSED; + let doCollapseFromActiveState = true; + + if (node.defaultIsCollapsed !== undefined) { + isCollapsed = node.defaultIsCollapsed; + doCollapseFromActiveState = false; + } + acc[_id] = { - isCollapsed: !node.isActive ?? DEFAULT_IS_COLLAPSED, + isCollapsed, isCollapsible: node.isCollapsible ?? DEFAULT_IS_COLLAPSIBLE, - doCollapseFromActiveState: true, + doCollapseFromActiveState, }; } return acc; @@ -354,7 +392,8 @@ export const NavigationSectionUI: FC = ({ navNode }) => { const [subItems, setSubItems] = useState(); const toggleAccordion = useCallback((id: string) => { - setItemsState((prev) => { + setItemsAccordionState((prev) => { + // if (prev[id]?.isCollapsed === undefined) return prev; const prevValue = prev[id]?.isCollapsed ?? DEFAULT_IS_COLLAPSED; return { ...prev, @@ -367,13 +406,15 @@ export const NavigationSectionUI: FC = ({ navNode }) => { }); }, []); - const setAccordionProps = useCallback( + const getAccordionProps = useCallback( ( id: string, _accordionProps?: Partial ): Partial | undefined => { - const isCollapsed = itemsState[id]?.isCollapsed ?? DEFAULT_IS_COLLAPSED; - const isCollapsible = itemsState[id]?.isCollapsible ?? DEFAULT_IS_COLLAPSIBLE; + const isCollapsed = itemsAccordionState[id]?.isCollapsed; + const isCollapsible = itemsAccordionState[id]?.isCollapsible; + + if (isCollapsed === undefined) return _accordionProps; // No state set yet let forceState: EuiAccordionProps['forceState'] = isCollapsed ? 'closed' : 'open'; if (!isCollapsible) forceState = 'open'; // Allways open if the accordion is not collapsible @@ -394,7 +435,7 @@ export const NavigationSectionUI: FC = ({ navNode }) => { return updated; }, - [itemsState, toggleAccordion] + [itemsAccordionState, toggleAccordion] ); const { items, isVisible } = useMemo(() => { @@ -404,9 +445,18 @@ export const NavigationSectionUI: FC = ({ navNode }) => { closePanel, isSideNavCollapsed, treeDepth: 0, - itemsState, + itemsAccordionState, + activeNodes, }); - }, [closePanel, isSideNavCollapsed, navNode, navigateToUrl, openPanel, itemsState]); + }, [ + navNode, + navigateToUrl, + openPanel, + closePanel, + isSideNavCollapsed, + itemsAccordionState, + activeNodes, + ]); const [props] = items; const { items: accordionItems } = props; @@ -416,58 +466,87 @@ export const NavigationSectionUI: FC = ({ navNode }) => { } /** - * Effect to set our internal state of each of the accordions (isCollapsed) based on the - * "isActive" state of the navNode. + * Effect to set the internal state of each of the accordions (isCollapsed) based on the + * "isActive" state of the navNode or if its path matches the URL location */ useEffect(() => { - setItemsState((prev) => { - return Object.entries(navNodesById).reduce((acc, [_id, node]) => { - if (node.children && (!prev[_id] || prev[_id].doCollapseFromActiveState)) { - acc[_id] = { - isCollapsed: !node.isActive ?? DEFAULT_IS_COLLAPSED, - isCollapsible: node.isCollapsible ?? DEFAULT_IS_COLLAPSIBLE, - doCollapseFromActiveState: true, - }; - } - return acc; - }, prev); + setItemsAccordionState((prev) => { + return Object.entries(navNodesById).reduce( + (acc, [_id, node]) => { + const prevState = prev[_id]; + + if ( + node.children && + node.renderAs !== 'item' && + (!prevState || prevState.doCollapseFromActiveState === true) + ) { + let nextIsActive = false; + let doCollapseFromActiveState = true; + + if (!prevState && node.defaultIsCollapsed !== undefined) { + nextIsActive = !node.defaultIsCollapsed; + doCollapseFromActiveState = false; + } else { + if (prevState?.doCollapseFromActiveState !== false) { + nextIsActive = isActiveFromUrl(node.path, activeNodes); + } else if (nextIsActive === undefined) { + nextIsActive = !DEFAULT_IS_COLLAPSED; + } + } + + acc[_id] = { + ...prevState, + isCollapsed: !nextIsActive, + isCollapsible: node.isCollapsible ?? DEFAULT_IS_COLLAPSIBLE, + doCollapseFromActiveState, + }; + } + return acc; + }, + { ...prev } + ); }); - }, [navNodesById]); + }, [navNodesById, activeNodes]); useEffect(() => { // Serializer to add recursively the accordionProps to each of the items // that will control its "open"/"closed" state + handler to toggle the state. const serializeAccordionItems = ( - _items?: EuiCollapsibleNavSubItemProps[] + _items?: EuiCollapsibleNavSubItemPropsEnhanced[] ): EuiCollapsibleNavSubItemProps[] | undefined => { if (!_items) return; - return _items.map((item: EuiCollapsibleNavSubItemProps) => { + return _items.map((item) => { if (item.renderItem) { return item; } + // @ts-ignore - TODO const parsed: EuiCollapsibleNavSubItemProps = { ...item, items: serializeAccordionItems(item.items), - accordionProps: setAccordionProps(item.id!, item.accordionProps), + accordionProps: + item.items !== undefined + ? getAccordionProps(item.path ?? item.id!, item.accordionProps) + : undefined, }; return parsed; }); }; setSubItems(serializeAccordionItems(accordionItems)); - }, [accordionItems, setAccordionProps]); + }, [accordionItems, getAccordionProps]); if (!isVisible) { return null; } return ( + // @ts-ignore - TODO ); -}; +}); diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/context.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/context.tsx index f9720a38a6655..7eee3515c49c3 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/context.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/context.tsx @@ -9,7 +9,6 @@ import React, { type FC, useCallback, useContext, useMemo, useState, ReactNode } from 'react'; import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; -import { nodePathToString } from '../../../utils'; import { DefaultContent } from './default_content'; import { ContentProvider, PanelNavNode } from './types'; @@ -54,7 +53,7 @@ export const PanelProvider: FC = ({ children, contentProvider, activeNode return null; } - const provided = contentProvider?.(nodePathToString(selectedNode)); + const provided = contentProvider?.(selectedNode.path); if (!provided) { return ; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/default_content.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/default_content.tsx index a678bad3aeaee..e3a45c7602257 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/default_content.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/default_content.tsx @@ -39,7 +39,7 @@ function serializeChildren(node: PanelNavNode): ChromeProjectNavigationNode[] | { id: 'root', title: '', - path: [...node.path, 'root'], + path: `${node.path}.root`, children: [...node.children], }, ]; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/navigation_panel.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/navigation_panel.tsx index ca118249d123b..ff79824176268 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/navigation_panel.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/navigation_panel.tsx @@ -38,7 +38,9 @@ export const NavigationPanel: FC = () => { ({ target }: Event) => { // Only close if we are not clicking on the currently selected nav node if ( - !(target as HTMLButtonElement).dataset.testSubj?.includes(`panelOpener-${selectedNode?.id}`) + !(target as HTMLButtonElement).dataset.testSubj?.includes( + `panelOpener-${selectedNode?.path}` + ) ) { close(); } diff --git a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx index f423ba98d04a7..e7d20a812d58f 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx @@ -8,9 +8,8 @@ import React, { FC, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import type { AppDeepLinkId, NodeDefinition } from '@kbn/core-chrome-browser'; +import type { NodeDefinition } from '@kbn/core-chrome-browser'; -import { getNavigationNodeId } from '../utils'; import { Navigation } from './components'; import type { GroupDefinition, @@ -116,55 +115,12 @@ const getDefaultNavigationTree = ( }; }; -/** - * Serialize a navigation node. Currently this handler only adds an autogenerated id if it's missing - * - * @param item The navigation node - * @returns The navigation node serialized - */ -function serializeNode< - T extends { - id?: string; - link?: LinkId; - children?: Array<{ id?: string; link?: LinkId }>; - }, - LinkId extends AppDeepLinkId = AppDeepLinkId ->(item: T, depth: number, index: number): T & { id: string } { - const id = getNavigationNodeId(item, () => `node-${depth}-${index}`); - const children = item.children?.map((_item, i) => serializeNode(_item, depth + 1, i)); - - return { - ...item, - id, - children, - }; -} - -const serializeNavigationTree = (navTree: NavigationTreeDefinition): NavigationTreeDefinition => { - const serialized: NavigationTreeDefinition = { ...navTree }; - - const serialize = (item: RootNavigationItemDefinition, index: number) => { - if (item.type === 'recentlyAccessed') return item; - return serializeNode(item, 0, index); - }; - - if (navTree.body) { - serialized.body = navTree.body.map(serialize); - } - - if (navTree.footer) { - serialized.footer = navTree.footer.map(serialize); - } - - return serialized; -}; - interface Props { dataTestSubj?: string; panelContentProvider?: ContentProvider; } -export const DefaultNavigation: FC = ({ +const DefaultNavigationComp: FC = ({ projectNavigationTree, navigationTree, dataTestSubj, @@ -174,14 +130,6 @@ export const DefaultNavigation: FC = ({ throw new Error('One of navigationTree or projectNavigationTree must be defined'); } - const navigationDefinition = useMemo(() => { - const definition = !navigationTree - ? getDefaultNavigationTree(projectNavigationTree!) - : navigationTree; - - return serializeNavigationTree(definition); - }, [navigationTree, projectNavigationTree]); - const renderNodes = useCallback( (nodes: Array = []) => { return nodes.map((navNode, i) => { @@ -194,28 +142,35 @@ export const DefaultNavigation: FC = ({ } if (isGroupDefinition(navNode)) { + // Recursively build the tree return ( - - {/* Recursively build the tree */} + {renderNodes(navNode.children)} ); } - return ; + return ; }); }, [] ); + const definitionToJSX = useMemo(() => { + const definition = !navigationTree + ? getDefaultNavigationTree(projectNavigationTree!) + : navigationTree; + + const { body, footer } = definition; + return { body: renderNodes(body), footer: Boolean(footer) ? renderNodes(footer) : null }; + }, [navigationTree, projectNavigationTree, renderNodes]); + return ( - <> - {renderNodes(navigationDefinition.body)} - {navigationDefinition.footer && ( - {renderNodes(navigationDefinition.footer)} - )} - + {definitionToJSX.body} + {definitionToJSX.footer && {definitionToJSX.footer}} ); }; + +export const DefaultNavigation = React.memo(DefaultNavigationComp) as typeof DefaultNavigationComp; diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts deleted file mode 100644 index f8d56bc785269..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts +++ /dev/null @@ -1,356 +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 { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import type { - AppDeepLinkId, - ChromeNavLink, - ChromeProjectNavigationNode, - CloudLinkId, - SideNavNodeStatus, -} from '@kbn/core-chrome-browser'; -import { CloudLinks } from '../../cloud_links'; - -import { useNavigation as useNavigationServices } from '../../services'; -import { getNavigationNodeId, isAbsoluteLink } from '../../utils'; -import { useNavigation } from '../components/navigation'; -import { NodePropsEnhanced, RegisterFunction, UnRegisterFunction } from '../types'; -import { useRegisterTreeNode } from './use_register_tree_node'; - -/** - * We don't have currently a way to know if a user has access to a Cloud section. - * TODO: This function will have to be revisited once we have an API from Cloud to know the user - * permissions. - */ -function hasUserAccessToCloudLink(): boolean { - return true; -} - -function getNodeStatus( - { - link, - deepLink, - cloudLink, - sideNavStatus, - }: { - link?: string; - deepLink?: ChromeNavLink; - cloudLink?: CloudLinkId; - sideNavStatus?: SideNavNodeStatus; - }, - { cloudLinks }: { cloudLinks: CloudLinks } -): SideNavNodeStatus | 'remove' { - if (link && !deepLink) { - // If a link is provided, but no deepLink is found, don't render anything - return 'remove'; - } - - if (cloudLink) { - if (!cloudLinks[cloudLink]) { - // Invalid cloudLinkId or link url has not been set in kibana.yml - return 'remove'; - } - if (!hasUserAccessToCloudLink()) return 'remove'; - } - - if (deepLink && deepLink.hidden) return 'hidden'; - - return sideNavStatus ?? 'visible'; -} - -function getTitleForNode< - LinkId extends AppDeepLinkId = AppDeepLinkId, - Id extends string = string, - ChildrenId extends string = Id ->( - navNode: NodePropsEnhanced, - { deepLink, cloudLinks }: { deepLink?: ChromeNavLink; cloudLinks: CloudLinks } -): string { - const { children } = navNode; - if (navNode.title) { - return navNode.title; - } - - if (typeof children === 'string') { - return children; - } - - if (deepLink?.title) { - return deepLink.title; - } - - if (navNode.cloudLink) { - return cloudLinks[navNode.cloudLink]?.title ?? ''; - } - - return ''; -} - -function validateNodeProps< - LinkId extends AppDeepLinkId = AppDeepLinkId, - Id extends string = string, - ChildrenId extends string = Id ->({ - id, - link, - href, - cloudLink, - renderAs, - appendHorizontalRule, - isGroup, -}: Omit, 'children'>) { - if (link && cloudLink) { - throw new Error( - `[Chrome navigation] Error in node [${id}]. Only one of "link" or "cloudLink" can be provided.` - ); - } - if (href && cloudLink) { - throw new Error( - `[Chrome navigation] Error in node [${id}]. Only one of "href" or "cloudLink" can be provided.` - ); - } - if (renderAs === 'panelOpener' && !link) { - throw new Error( - `[Chrome navigation] Error in node [${id}]. If renderAs is set to "panelOpener", a "link" must also be provided.` - ); - } - if (renderAs === 'item' && !link) { - throw new Error( - `[Chrome navigation] Error in node [${id}]. If renderAs is set to "item", a "link" must also be provided.` - ); - } - if (appendHorizontalRule && !isGroup) { - throw new Error( - `[Chrome navigation] Error in node [${id}]. "appendHorizontalRule" can only be added for group with children.` - ); - } -} - -function createInternalNavNode< - LinkId extends AppDeepLinkId = AppDeepLinkId, - Id extends string = string, - ChildrenId extends string = Id ->( - id: string, - _navNode: NodePropsEnhanced, - deepLinks: Readonly, - path: string[] | null, - isActive: boolean, - { cloudLinks }: { cloudLinks: CloudLinks } -): ChromeProjectNavigationNode | null { - validateNodeProps(_navNode); - - const { children, link, cloudLink, ...navNode } = _navNode; - const deepLink = deepLinks.find((dl) => dl.id === link); - const sideNavStatus = getNodeStatus( - { - link, - deepLink, - cloudLink, - sideNavStatus: navNode.sideNavStatus, - }, - { cloudLinks } - ); - const title = getTitleForNode(_navNode, { deepLink, cloudLinks }); - const href = cloudLink ? cloudLinks[cloudLink]?.href : _navNode.href; - - if (href && !isAbsoluteLink(href)) { - throw new Error(`href must be an absolute URL. Node id [${id}].`); - } - - if (sideNavStatus === 'remove') { - return null; - } - - return { - ...navNode, - id, - path: path ?? [], - title: title ?? '', - deepLink, - href, - isActive, - sideNavStatus, - }; -} - -function isSamePath(pathA: string[] | null, pathB: string[] | null) { - if (pathA === null || pathB === null) { - return false; - } - const pathAToString = pathA.join('.'); - const pathBToString = pathB.join('.'); - return pathAToString === pathBToString; -} - -export const useInitNavNode = < - LinkId extends AppDeepLinkId = AppDeepLinkId, - Id extends string = string, - ChildrenId extends string = Id ->( - node: Omit, 'children'>, - { cloudLinks }: { cloudLinks: CloudLinks } -) => { - const { isActive: isActiveControlled } = node; - - /** - * Map of children nodes - */ - const [childrenNodes, setChildrenNodes] = useState>( - {} - ); - - const isMounted = useRef(false); - - /** - * Reference to the unregister function - */ - const unregisterRef = useRef(); - - /** - * Map to keep track of the order of the children when they mount. - * This allows us to keep in sync the nav tree sent to the Chrome service - * with the order of the DOM elements - */ - const orderChildrenRef = useRef>({}); - - /** - * Index to keep track of the order of the children when they mount. - */ - const idx = useRef(0); - - /** - * The current node path, including all of its parents. We'll use it to match it against - * the list of active routes based on current URL location (passed by the Chrome service) - */ - const [nodePath, setNodePath] = useState(null); - const [isActiveState, setIsActive] = useState(false); - const isActive = isActiveControlled ?? isActiveState; - - const { navLinks$ } = useNavigationServices(); - const deepLinks = useObservable(navLinks$, []); - const { register: registerNodeOnParent } = useRegisterTreeNode(); - const { activeNodes } = useNavigation(); - - const id = getNavigationNodeId(node); - - const internalNavNode = useMemo( - () => createInternalNavNode(id, node, deepLinks, nodePath, isActive, { cloudLinks }), - [node, id, deepLinks, nodePath, isActive, cloudLinks] - ); - - // Register the node on the parent whenever its properties change or whenever - // a child node is registered. - const register = useCallback(() => { - if (!internalNavNode) { - return; - } - - const children = Object.values(childrenNodes).sort((a, b) => { - const aOrder = orderChildrenRef.current[a.id]; - const bOrder = orderChildrenRef.current[b.id]; - return aOrder - bOrder; - }); - - const { unregister, path } = registerNodeOnParent({ - ...internalNavNode, - children: children.length ? children : undefined, - }); - - setNodePath((prev) => { - if (!isSamePath(prev, path)) { - return path; - } - return prev; - }); - - unregisterRef.current = unregister; - }, [internalNavNode, childrenNodes, registerNodeOnParent]); - - // Un-register from the parent. This will happen when the node is unmounted or if the deeplink - // is not active anymore. - const unregister = useCallback(() => { - if (unregisterRef.current) { - unregisterRef.current(id); - unregisterRef.current = undefined; - } - }, [id]); - - const registerChildNode = useCallback( - (childNode) => { - if (orderChildrenRef.current[childNode.id] === undefined) { - orderChildrenRef.current[childNode.id] = idx.current++; - } - - const childPath = nodePath ? [...nodePath, childNode.id] : []; - - setChildrenNodes((prev) => { - return { - ...prev, - [childNode.id]: { - ...childNode, - path: childPath, - }, - }; - }); - - return { - unregister: (childId: string) => { - setChildrenNodes((prev) => { - const updatedItems = { ...prev }; - delete updatedItems[childId]; - return updatedItems; - }); - }, - path: childPath, - }; - }, - [nodePath] - ); - - useEffect(() => { - const updatedIsActive = activeNodes.reduce((acc, nodesBranch) => { - return acc === true ? acc : nodesBranch.some((_node) => isSamePath(_node.path, nodePath)); - }, false); - - setIsActive(updatedIsActive); - }, [activeNodes, nodePath]); - - /** Register when mounting and whenever the internal nav node changes */ - useEffect(() => { - if (!isMounted.current) { - return; - } - - if (internalNavNode) { - register(); - } else { - unregister(); - } - }, [unregister, register, internalNavNode]); - - /** Unregister when unmounting */ - useEffect(() => { - isMounted.current = true; - return () => { - isMounted.current = false; - unregister(); - }; - }, [unregister]); - - return useMemo( - () => ({ - navNode: internalNavNode, - path: nodePath, - registerChildNode, - childrenNodes, - }), - [internalNavNode, registerChildNode, nodePath, childrenNodes] - ); -}; diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_register_tree_node.ts b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_register_tree_node.ts deleted file mode 100644 index 5ad690b2de2e1..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_register_tree_node.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 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 { useMemo } from 'react'; - -import { useNavigation } from '../components/navigation'; -import { useNavigationGroup } from '../components/navigation_group'; - -/** - * Helper hook that will proxy the correct "register" handler. - * It first tries to the closest parent group, if not found it will use the root register. - */ -export const useRegisterTreeNode = () => { - const root = useNavigation(); - const group = useNavigationGroup(false); - const register = group ? group.register : root.register; - - return useMemo( - () => ({ - register, - }), - [register] - ); -}; diff --git a/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx b/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx index be749a5cac9bb..def69d3c9df52 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx @@ -7,26 +7,22 @@ */ import { action } from '@storybook/addon-actions'; -import { useState as useStateStorybook } from '@storybook/addons'; import { ComponentMeta } from '@storybook/react'; import React, { EventHandler, FC, MouseEvent, useState, useEffect } from 'react'; -import { BehaviorSubject, of } from 'rxjs'; +import { of } from 'rxjs'; import { EuiButton, EuiCollapsibleNavBeta, EuiCollapsibleNavBetaProps, - EuiFlexGroup, - EuiFlexItem, EuiHeader, EuiHeaderSection, EuiLink, EuiPageTemplate, EuiText, - EuiTitle, } from '@elastic/eui'; -import type { ChromeNavLink, ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; import { NavigationStorybookMock, navLinksMock } from '../../mocks'; import mdx from '../../README.mdx'; import type { NavigationServices } from '../../types'; @@ -34,7 +30,7 @@ import { NavigationProvider } from '../services'; import { Navigation } from './components'; import { DefaultNavigation } from './default_navigation'; import { getPresets } from './nav_tree_presets'; -import type { GroupDefinition, ProjectNavigationDefinition } from './types'; +import type { ProjectNavigationDefinition } from './types'; import { ContentProvider } from './components/panel'; const storybookMock = new NavigationStorybookMock(); @@ -127,6 +123,13 @@ const deepLinks: ChromeNavLink[] = [ createDeepLink('group:settings.tracing'), ]; +const deepLinks$ = of({ + ...[...navLinksMock, ...deepLinks].reduce>((acc, navLink) => { + acc[navLink.id] = navLink; + return acc; + }, {}), +}); + const simpleNavigationDefinition: ProjectNavigationDefinition = { projectNavigationTree: [ { @@ -182,7 +185,7 @@ const simpleNavigationDefinition: ProjectNavigationDefinition = { export const SimpleObjectDefinition = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -330,7 +333,7 @@ const groupExamplesDefinition: ProjectNavigationDefinition = { export const GroupsExamples = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -342,9 +345,11 @@ export const GroupsExamples = (args: NavigationServices) => { return ( - - - + {({ isCollapsed }) => ( + + + + )} ); }; @@ -401,6 +406,7 @@ const navigationDefinition: ProjectNavigationDefinition = { { id: 'group:settings-2', title: 'Settings as nav Item', + link: 'item1', renderAs: 'item', // Render just like any other item, even if it has children children: [ { @@ -520,6 +526,7 @@ const navigationDefinition: ProjectNavigationDefinition = { id: 'test_all_hidden', title: 'Test group render as Item', renderAs: 'item', + link: 'item1', children: [ { id: 'test.item1', @@ -576,7 +583,7 @@ const navigationDefinition: ProjectNavigationDefinition = { export const ComplexObjectDefinition = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -891,6 +898,7 @@ const navigationDefinitionWithPanel: ProjectNavigationDefinition = { { title: 'Group renders as "item" (2)', id: 'group2.renderAsItem', + link: 'item1', renderAs: 'item', children: [ { @@ -945,6 +953,7 @@ const navigationDefinitionWithPanel: ProjectNavigationDefinition = { children: [ { id: 'group2-B', + link: 'item1', title: 'Group renders as "item" (3)', renderAs: 'item', // This group renders as a normal item children: [ @@ -978,6 +987,7 @@ const navigationDefinitionWithPanel: ProjectNavigationDefinition = { }, { title: 'Yet another group as item', + link: 'item1', renderAs: 'item', children: [ { @@ -1076,7 +1086,7 @@ const navigationDefinitionWithPanel: ProjectNavigationDefinition = { export const ObjectDefinitionWithPanel = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -1100,10 +1110,69 @@ export const ObjectDefinitionWithPanel = (args: NavigationServices) => { ); }; +export const WithUIComponentsTiny = (args: NavigationServices) => { + const services = storybookMock.getServices({ + ...args, + deepLinks$, + onProjectNavigationChange: (updated) => { + action('Update chrome navigation')(JSON.stringify(updated, null, 2)); + }, + recentlyAccessed$: of([ + { label: 'This is an example', link: '/app/example/39859', id: '39850' }, + { label: 'Another example', link: '/app/example/5235', id: '5235' }, + ]), + }); + + return ( + + {({ isCollapsed }) => ( + + + + + id="item1" link="item1" /> + + + id="item2" link="item1" title="YEAH!!" icon="launch" /> + + + + + + + + + + + + + + + )} + + ); +}; + export const WithUIComponents = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -1127,7 +1196,7 @@ export const WithUIComponents = (args: NavigationServices) => { defaultIsCollapsed={false} > id="item1" link="item1" /> - + {/* {(navNode) => { return (
    @@ -1135,7 +1204,7 @@ export const WithUIComponents = (args: NavigationServices) => {
    ); }} -
    +
    */}
    Title in ReactNode @@ -1224,7 +1293,7 @@ export const WithUIComponents = (args: NavigationServices) => { export const MinimalUI = (args: NavigationServices) => { const services = storybookMock.getServices({ ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), + deepLinks$, onProjectNavigationChange: (updated) => { action('Update chrome navigation')(JSON.stringify(updated, null, 2)); }, @@ -1272,230 +1341,6 @@ export const MinimalUI = (args: NavigationServices) => { ); }; -export const CreativeUI = (args: NavigationServices) => { - const services = storybookMock.getServices({ - ...args, - navLinks$: of([...navLinksMock, ...deepLinks]), - onProjectNavigationChange: (updated) => { - action('Update chrome navigation')(JSON.stringify(updated, null, 2)); - }, - recentlyAccessed$: of([ - { label: 'This is an example', link: '/app/example/39859', id: '39850' }, - { label: 'Another example', link: '/app/example/5235', id: '5235' }, - ]), - }); - - return ( - - - - - - - - -

    Hello!

    -
    - - -
    - -

    - As you can see there is really no limit in what UI you can create! -
    -

    -

    - Have fun! -

    - - - - - - - - - - ); -}; - -export const UpdatingState = (args: NavigationServices) => { - const simpleGroupDef: GroupDefinition = { - type: 'navGroup', - id: 'observability_project_nav', - title: 'Observability', - icon: 'logoObservability', - children: [ - { - id: 'aiops', - title: 'AIOps', - icon: 'branch', - children: [ - { - title: 'Anomaly detection', - id: 'ml:anomalyDetection', - link: 'ml:anomalyDetection', - }, - { - title: 'Log Rate Analysis', - id: 'ml:logRateAnalysis', - link: 'ml:logRateAnalysis', - }, - { - title: 'Change Point Detections', - link: 'ml:changePointDetections', - id: 'ml:changePointDetections', - }, - { - title: 'Job Notifications', - link: 'ml:notifications', - id: 'ml:notifications', - }, - ], - }, - { - id: 'project_settings_project_nav', - title: 'Project settings', - icon: 'gear', - children: [ - { id: 'management', link: 'management' }, - { id: 'integrations', link: 'integrations' }, - { id: 'fleet', link: 'fleet' }, - ], - }, - ], - }; - const firstSection = simpleGroupDef.children![0]; - const firstSectionFirstChild = firstSection.children![0]; - const secondSection = simpleGroupDef.children![1]; - const secondSectionFirstChild = secondSection.children![0]; - - const activeNodeSets: ChromeProjectNavigationNode[][][] = [ - [ - [ - { - ...simpleGroupDef, - path: [simpleGroupDef.id], - } as unknown as ChromeProjectNavigationNode, - { - ...firstSection, - path: [simpleGroupDef.id, firstSection.id], - } as unknown as ChromeProjectNavigationNode, - { - ...firstSectionFirstChild, - path: [simpleGroupDef.id, firstSection.id, firstSectionFirstChild.id], - } as unknown as ChromeProjectNavigationNode, - ], - ], - [ - [ - { - ...simpleGroupDef, - path: [simpleGroupDef.id], - } as unknown as ChromeProjectNavigationNode, - { - ...secondSection, - path: [simpleGroupDef.id, secondSection.id], - } as unknown as ChromeProjectNavigationNode, - { - ...secondSectionFirstChild, - path: [simpleGroupDef.id, secondSection.id, secondSectionFirstChild.id], - } as unknown as ChromeProjectNavigationNode, - ], - ], - ]; - - // use state to track which element of activeNodeSets is active - const [activeNodeIndex, setActiveNodeIndex] = useStateStorybook(0); - const changeActiveNode = () => { - const value = (activeNodeIndex + 1) % 2; // toggle between 0 and 1 - setActiveNodeIndex(value); - }; - - const activeNodes$ = new BehaviorSubject([]); - activeNodes$.next(activeNodeSets[activeNodeIndex]); - - const services = storybookMock.getServices({ - ...args, - activeNodes$, - navLinks$: of([...navLinksMock, ...deepLinks]), - onProjectNavigationChange: (updated) => { - action('Update chrome navigation')(JSON.stringify(updated, null, 2)); - }, - }); - - return ( - - - - - - ); -}; - export default { title: 'Chrome/Navigation', description: 'Navigation container to render items for cross-app linking', diff --git a/packages/shared-ux/chrome/navigation/src/ui/types.ts b/packages/shared-ux/chrome/navigation/src/ui/types.ts index 9051cc3747bf0..768155ced3050 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/types.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/types.ts @@ -30,26 +30,15 @@ export interface NodeProps< * Children of the node. For Navigation.Item (only) it allows a function to be set. * This function will receive the ChromeProjectNavigationNode object */ - children?: ((navNode: ChromeProjectNavigationNode) => ReactNode) | ReactNode; -} - -/** - * @internal - * - * Internally we enhance the Props passed to the Navigation.Item component. - */ -export interface NodePropsEnhanced< - LinkId extends AppDeepLinkId = AppDeepLinkId, - Id extends string = string, - ChildrenId extends string = Id -> extends NodeProps { - /** - * Forces the node to be active. This is used to force a collapisble nav group to be open - * even if the URL does not match any of the nodes in the group. - */ - isActive?: boolean; - /** Flag to indicate if the navigation node is a group or not */ - isGroup: boolean; + children?: ReactNode; + /** @internal - Prop internally controlled, don't use it. */ + parentNodePath?: string; + /** @internal - Prop internally controlled, don't use it. */ + rootIndex?: number; + /** @internal - Prop internally controlled, don't use it. */ + treeDepth?: number; + /** @internal - Prop internally controlled, don't use it. */ + index?: number; } /** The preset that can be pass to the NavigationBucket component */ @@ -181,16 +170,14 @@ export interface ProjectNavigationDefinition< * * Function to unregister a navigation node from its parent. */ -export type UnRegisterFunction = (id: string) => void; +export type UnRegisterFunction = () => void; /** * @internal * * A function to register a navigation node on its parent. */ -export type RegisterFunction = (navNode: ChromeProjectNavigationNode) => { - /** The function to unregister the node. */ - unregister: UnRegisterFunction; - /** The full path of the node in the navigation tree. */ - path: string[]; -}; +export type RegisterFunction = ( + navNode: ChromeProjectNavigationNode, + order?: number +) => UnRegisterFunction; diff --git a/packages/shared-ux/chrome/navigation/src/utils.ts b/packages/shared-ux/chrome/navigation/src/utils.ts index 8322fe797590b..efbf3c0e2a42e 100644 --- a/packages/shared-ux/chrome/navigation/src/utils.ts +++ b/packages/shared-ux/chrome/navigation/src/utils.ts @@ -6,11 +6,16 @@ * Side Public License, v 1. */ +import React, { type ReactNode } from 'react'; import type { ChromeProjectNavigationNode, NodeDefinition } from '@kbn/core-chrome-browser'; +import { NavigationFooter } from './ui/components/navigation_footer'; +import { NavigationGroup } from './ui/components/navigation_group'; +import { NavigationItem } from './ui/components/navigation_item'; +import { RecentlyAccessed } from './ui/components/recently_accessed'; let uniqueId = 0; -function generateUniqueNodeId() { +export function generateUniqueNodeId() { const id = `node${uniqueId++}`; return id; } @@ -19,15 +24,6 @@ export function isAbsoluteLink(link: string) { return link.startsWith('http://') || link.startsWith('https://'); } -export function nodePathToString( - node?: T -): T extends { path?: string[]; id: string } ? string : undefined { - if (!node) return undefined as T extends { path?: string[]; id: string } ? string : undefined; - return (node.path ? node.path.join('.') : node.id) as T extends { path?: string[]; id: string } - ? string - : undefined; -} - export function isGroupNode({ children }: Pick) { return children !== undefined; } @@ -50,3 +46,37 @@ export function getNavigationNodeHref({ }: Pick): string | undefined { return deepLink?.url ?? href; } + +function isSamePath(pathA: string | null, pathB: string | null) { + if (pathA === null || pathB === null) { + return false; + } + return pathA === pathB; +} + +export function isActiveFromUrl(nodePath: string, activeNodes: ChromeProjectNavigationNode[][]) { + return activeNodes.reduce((acc, nodesBranch) => { + return acc === true ? acc : nodesBranch.some((branch) => isSamePath(branch.path, nodePath)); + }, false); +} + +type ChildType = 'item' | 'group' | 'recentlyAccessed' | 'footer' | 'unknown'; + +export const getChildType = (child: ReactNode): ChildType => { + if (!React.isValidElement(child)) { + return 'unknown'; + } + + switch (child.type) { + case NavigationItem: + return 'item'; + case NavigationGroup: + return 'group'; + case RecentlyAccessed: + return 'recentlyAccessed'; + case NavigationFooter: + return 'footer'; + default: + return 'unknown'; + } +}; diff --git a/packages/shared-ux/chrome/navigation/types/index.ts b/packages/shared-ux/chrome/navigation/types/index.ts index e162e395362a9..43ff4083ba414 100644 --- a/packages/shared-ux/chrome/navigation/types/index.ts +++ b/packages/shared-ux/chrome/navigation/types/index.ts @@ -24,7 +24,7 @@ import type { CloudLinks } from '../src/cloud_links'; export interface NavigationServices { basePath: BasePathService; recentlyAccessed$: Observable; - navLinks$: Observable>; + deepLinks$: Observable>>; navIsOpen: boolean; navigateToUrl: NavigateToUrlFn; onProjectNavigationChange: (chromeProjectNavigation: ChromeProjectNavigation) => void; diff --git a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.component.tsx b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.component.tsx index 96e80af246511..3705b5885f1ab 100644 --- a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.component.tsx +++ b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.component.tsx @@ -29,6 +29,7 @@ export const RedirectAppLinks: FC = ({ children, navigateToUrl, currentAppId, + ...containerProps }) => { const containerRef = useRef(null); @@ -50,6 +51,7 @@ export const RedirectAppLinks: FC = ({ ref={containerRef} css={redirectAppLinksStyles} data-test-subj="kbnRedirectAppLink" + {...containerProps} > {children}
    diff --git a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx index 9a069881b2128..da227ab023cbb 100644 --- a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx +++ b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.container.tsx @@ -7,6 +7,7 @@ */ import React, { FC } from 'react'; +import type { RedirectAppLinksComponentProps } from '@kbn/shared-ux-link-redirect-app-types'; import { useServices } from './services'; import { RedirectAppLinks as Component } from './redirect_app_links.component'; @@ -22,6 +23,11 @@ import { RedirectAppLinks as Component } from './redirect_app_links.component'; * * ``` */ -export const RedirectAppLinks: FC<{}> = ({ children }) => ( - {children} +export const RedirectAppLinks: FC> = ({ + children, + ...props +}) => ( + + {children} + ); diff --git a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.tsx b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.tsx index 2909dcdbda17d..89d8a61531e9b 100644 --- a/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.tsx +++ b/packages/shared-ux/link/redirect_app/impl/src/redirect_app_links.tsx @@ -25,10 +25,11 @@ const isKibanaContract = (services: any): services is RedirectAppLinksKibanaDepe * with which consumers can wrap their components or solutions. */ export const RedirectAppLinks: FC = ({ children, ...props }) => { - const container = {children}; - if (isKibanaContract(props)) { - const { coreStart } = props; + const { coreStart, ...containerProps } = props; + const container = ( + {children} + ); return ( {container} @@ -36,7 +37,10 @@ export const RedirectAppLinks: FC = ({ children, ...props ); } - const { navigateToUrl, currentAppId } = props; + const { navigateToUrl, currentAppId, ...containerProps } = props; + const container = ( + {children} + ); return ( {container} diff --git a/packages/shared-ux/link/redirect_app/types/index.d.ts b/packages/shared-ux/link/redirect_app/types/index.d.ts index 186e86af89435..01899edea65d3 100644 --- a/packages/shared-ux/link/redirect_app/types/index.d.ts +++ b/packages/shared-ux/link/redirect_app/types/index.d.ts @@ -32,7 +32,11 @@ export interface RedirectAppLinksKibanaDependencies { } /** Props for the `RedirectAppLinks` component. */ -export type RedirectAppLinksProps = RedirectAppLinksServices | RedirectAppLinksKibanaDependencies; +export type RedirectAppLinksProps = ( + | RedirectAppLinksServices + | RedirectAppLinksKibanaDependencies +) & + DetailedHTMLProps, HTMLDivElement>; /** Props for the `RedirectAppLinksComponent`. */ export interface RedirectAppLinksComponentProps diff --git a/renovate.json b/renovate.json index 63ca5d0ba8a97..4a725ce80a5f5 100644 --- a/renovate.json +++ b/renovate.json @@ -458,13 +458,38 @@ "matchPackagePatterns": [ "^@storybook" ], + "excludePackageNames": [ + "@storybook/testing-react" + ], "labels": [ "Team:Operations", - "release_note:skip" + "release_note:skip", + "ci:build-storybooks", + "backport:skip" ], "enabled": true, "allowedVersions": "<7.0" }, + { + "groupName": "@storybook/testing-react", + "reviewers": [ + "team:kibana-operations" + ], + "matchBaseBranches": [ + "main" + ], + "matchPackageNames": [ + "@storybook/testing-react" + ], + "labels": [ + "Team:Operations", + "release_note:skip", + "ci:build-storybooks", + "backport:skip" + ], + "enabled": true, + "allowedVersions": "<2.0" + }, { "groupName": "react-query", "packageNames": [ diff --git a/scripts/es.js b/scripts/es.js index 1fcd221c97904..1cee27b7685b5 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -20,6 +20,7 @@ kbnEs 'source-path': resolve(__dirname, '../../elasticsearch'), 'base-path': resolve(__dirname, '../.es'), ssl: false, + kibanaUrl: 'https://localhost:5601/', }) .catch(function (e) { console.error(e); diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 911eecd45a9fb..75a18378f56b3 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -109,6 +109,38 @@ export function applyConfigOverrides(rawConfig, opts, extraCliOptions) { if (opts.dev) { if (opts.serverless) { setServerlessKibanaDevServiceAccountIfPossible(get, set, opts); + + // Load mock identity provider plugin and configure realm if supported (ES only supports SAML when run with SSL) + if (opts.ssl && canRequire('@kbn/mock-idp-plugin/common')) { + // Ensure the plugin is loaded in dynamically to exclude from production build + const { + MOCK_IDP_PLUGIN_PATH, + MOCK_IDP_REALM_NAME, + } = require('@kbn/mock-idp-plugin/common'); + + if (has('server.basePath')) { + console.log( + `Custom base path is not supported when running in Serverless, it will be removed.` + ); + _.unset(rawConfig, 'server.basePath'); + } + + set('plugins.paths', _.compact([].concat(get('plugins.paths'), MOCK_IDP_PLUGIN_PATH))); + set(`xpack.security.authc.providers.saml.${MOCK_IDP_REALM_NAME}`, { + order: Number.MAX_SAFE_INTEGER, + realm: MOCK_IDP_REALM_NAME, + icon: 'user', + description: 'Continue as Test User', + hint: 'Allows testing serverless user roles', + }); + // Add basic realm since defaults won't be applied when a provider has been configured + if (!has('xpack.security.authc.providers.basic')) { + set('xpack.security.authc.providers.basic.basic', { + order: 0, + enabled: true, + }); + } + } } if (!has('elasticsearch.serviceAccountToken') && opts.devCredentials !== false) { @@ -274,7 +306,9 @@ export default function (program) { // We can tell users they only have to run with `yarn start --run-examples` to get those // local links to work. Similar to what we do for "View in Console" links in our // elastic.co links. - basePath: opts.runExamples ? false : !!opts.basePath, + // We also want to run without base path when running in serverless mode so that Elasticsearch can + // connect to Kibana's mock identity provider. + basePath: opts.runExamples || isServerlessMode ? false : !!opts.basePath, optimize: !!opts.optimize, disableOptimizer: !opts.optimizer, oss: !!opts.oss, diff --git a/src/cli/tsconfig.json b/src/cli/tsconfig.json index ebbbc19f75c79..3b3c0854975d3 100644 --- a/src/cli/tsconfig.json +++ b/src/cli/tsconfig.json @@ -17,6 +17,7 @@ "@kbn/config", "@kbn/dev-utils", "@kbn/apm-config-loader", + "@kbn/mock-idp-plugin", ], "exclude": [ "target/**/*", diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 2c7d540132139..43920d3bbbe23 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -57,7 +57,7 @@ describe('checking migration metadata changes on all registered SO types', () => Object { "action": "cc93fe2c0c76e57c2568c63170e05daea897c136", "action_task_params": "96e27e7f4e8273ffcd87060221e2b75e81912dd5", - "alert": "dc710bc17dfc98a9a703d388569abccce5f8bf07", + "alert": "3a67d3f1db80af36bd57aaea47ecfef87e43c58f", "api_key_pending_invalidation": "1399e87ca37b3d3a65d269c924eda70726cfe886", "apm-custom-dashboards": "b67128f78160c288bd7efe25b2da6e2afd5e82fc", "apm-indices": "8a2d68d415a4b542b26b0d292034a28ffac6fed4", @@ -84,7 +84,7 @@ describe('checking migration metadata changes on all registered SO types', () => "dashboard": "0611794ce10d25a36da0770c91376c575e92e8f2", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", - "epm-packages": "2449bb565f987eff70b1b39578bb17e90c404c6e", + "epm-packages": "c23d3d00c051a08817335dba26f542b64b18a56a", "epm-packages-assets": "7a3e58efd9a14191d0d1a00b8aaed30a145fd0b1", "event-annotation-group": "715ba867d8c68f3c9438052210ea1c30a9362582", "event_loop_delays_daily": "01b967e8e043801357503de09199dfa3853bab88", @@ -106,7 +106,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", "ingest-agent-policies": "7633e578f60c074f8267bc50ec4763845e431437", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", - "ingest-outputs": "8546f1123ec30dcbd6f238f72729c5f1656a4d9b", + "ingest-outputs": "20bd44ce6016079c3b28f1b2bc241e7715be48f8", "ingest-package-policies": "f4c2767e852b700a8b82678925b86bac08958b43", "ingest_manager_settings": "64955ef1b7a9ffa894d4bb9cf863b5602bfa6885", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", diff --git a/src/core/server/integration_tests/elasticsearch/error_logging.test.ts b/src/core/server/integration_tests/elasticsearch/error_logging.test.ts index 8cd4f5ae41134..f8deaa41ee862 100644 --- a/src/core/server/integration_tests/elasticsearch/error_logging.test.ts +++ b/src/core/server/integration_tests/elasticsearch/error_logging.test.ts @@ -86,6 +86,7 @@ describe('Error logging', () => { name: 'ResponseError', process: { pid: expect.any(Number), + uptime: expect.any(Number), }, }); } diff --git a/src/core/server/integration_tests/http/http_server.test.ts b/src/core/server/integration_tests/http/http_server.test.ts index da10b231b7a22..b901f7f85cb6c 100644 --- a/src/core/server/integration_tests/http/http_server.test.ts +++ b/src/core/server/integration_tests/http/http_server.test.ts @@ -52,7 +52,7 @@ describe('Http server', () => { beforeEach(async () => { shutdownTimeout = config.shutdownTimeout.asMilliseconds(); - const { registerRouter, server: innerServer } = await server.setup(config); + const { registerRouter, server: innerServer } = await server.setup({ config$: of(config) }); innerServerListener = innerServer.listener; const router = new Router('', logger, enhanceWithContext, { diff --git a/src/core/server/integration_tests/http/set_tls_config.test.ts b/src/core/server/integration_tests/http/set_tls_config.test.ts new file mode 100644 index 0000000000000..6c198d820670f --- /dev/null +++ b/src/core/server/integration_tests/http/set_tls_config.test.ts @@ -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 supertest from 'supertest'; +import { KBN_CERT_PATH, KBN_KEY_PATH, ES_KEY_PATH, ES_CERT_PATH } from '@kbn/dev-utils'; +import { + createServer, + getListenerOptions, + getServerOptions, + setTlsConfig, +} from '@kbn/server-http-tools'; +import { + HttpConfig, + config as httpConfig, + cspConfig, + externalUrlConfig, +} from '@kbn/core-http-server-internal'; +import { flattenCertificateChain, fetchPeerCertificate, isServerTLS } from './tls_utils'; + +describe('setTlsConfig', () => { + const CSP_CONFIG = cspConfig.schema.validate({}); + const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({}); + + beforeAll(() => { + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + }); + + it('replaces the TLS configuration on the HAPI server', async () => { + const rawHttpConfig = httpConfig.schema.validate({ + name: 'kibana', + host: '127.0.0.1', + port: 10002, + ssl: { + enabled: true, + certificate: KBN_CERT_PATH, + key: KBN_KEY_PATH, + cipherSuites: ['TLS_AES_256_GCM_SHA384'], + redirectHttpFromPort: 10003, + }, + shutdownTimeout: '1s', + }); + const firstConfig = new HttpConfig(rawHttpConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG); + + const serverOptions = getServerOptions(firstConfig); + const listenerOptions = getListenerOptions(firstConfig); + const server = createServer(serverOptions, listenerOptions); + + server.route({ + method: 'GET', + path: '/', + handler: (request, toolkit) => { + return toolkit.response('ok'); + }, + }); + + await server.start(); + + const listener = server.listener; + + // force TS to understand what we're working with. + if (!isServerTLS(listener)) { + throw new Error('Server should be a TLS server'); + } + + const certificate = await fetchPeerCertificate(firstConfig.host, firstConfig.port); + const certificateChain = flattenCertificateChain(certificate); + + expect(isServerTLS(listener)).toEqual(true); + expect(certificateChain.length).toEqual(1); + expect(certificateChain[0].subject.CN).toEqual('kibana'); + + await supertest(listener).get('/').expect(200); + + const secondRawConfig = httpConfig.schema.validate({ + name: 'kibana', + host: '127.0.0.1', + port: 10002, + ssl: { + enabled: true, + certificate: ES_CERT_PATH, + key: ES_KEY_PATH, + cipherSuites: ['TLS_AES_256_GCM_SHA384'], + redirectHttpFromPort: 10003, + }, + shutdownTimeout: '1s', + }); + + const secondConfig = new HttpConfig(secondRawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG); + + setTlsConfig(server, secondConfig.ssl); + + const secondCertificate = await fetchPeerCertificate(firstConfig.host, firstConfig.port); + const secondCertificateChain = flattenCertificateChain(secondCertificate); + + expect(secondCertificateChain.length).toEqual(1); + expect(secondCertificateChain[0].subject.CN).toEqual('elasticsearch'); + + await supertest(listener).get('/').expect(200); + + await server.stop(); + }); +}); diff --git a/src/core/server/integration_tests/http/tls_config_reload.test.ts b/src/core/server/integration_tests/http/tls_config_reload.test.ts new file mode 100644 index 0000000000000..b0c8f6abf5e5c --- /dev/null +++ b/src/core/server/integration_tests/http/tls_config_reload.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright 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 supertest from 'supertest'; +import { duration } from 'moment'; +import { BehaviorSubject, of } from 'rxjs'; +import { KBN_CERT_PATH, KBN_KEY_PATH, ES_KEY_PATH, ES_CERT_PATH } from '@kbn/dev-utils'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { Router } from '@kbn/core-http-router-server-internal'; +import { + HttpServer, + HttpConfig, + config as httpConfig, + cspConfig, + externalUrlConfig, +} from '@kbn/core-http-server-internal'; +import { isServerTLS, flattenCertificateChain, fetchPeerCertificate } from './tls_utils'; + +const CSP_CONFIG = cspConfig.schema.validate({}); +const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({}); +const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); + +describe('HttpServer - TLS config', () => { + let server: HttpServer; + let logger: ReturnType; + + beforeAll(() => { + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + }); + + beforeEach(() => { + const loggingService = loggingSystemMock.create(); + logger = loggingSystemMock.createLogger(); + server = new HttpServer(loggingService, 'tests', of(duration('1s'))); + }); + + it('supports dynamic reloading of the TLS configuration', async () => { + const rawHttpConfig = httpConfig.schema.validate({ + name: 'kibana', + host: '127.0.0.1', + port: 10002, + ssl: { + enabled: true, + certificate: KBN_CERT_PATH, + key: KBN_KEY_PATH, + cipherSuites: ['TLS_AES_256_GCM_SHA384'], + redirectHttpFromPort: 10003, + }, + shutdownTimeout: '1s', + }); + const firstConfig = new HttpConfig(rawHttpConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG); + + const config$ = new BehaviorSubject(firstConfig); + + const { server: innerServer, registerRouter } = await server.setup({ config$ }); + const listener = innerServer.listener; + + const router = new Router('', logger, enhanceWithContext, { + isDev: false, + versionedRouteResolution: 'oldest', + }); + router.get( + { + path: '/', + validate: false, + }, + async (ctx, req, res) => { + return res.ok({ + body: 'ok', + }); + } + ); + registerRouter(router); + + await server.start(); + + // force TS to understand what we're working with. + if (!isServerTLS(listener)) { + throw new Error('Server should be a TLS server'); + } + + const certificate = await fetchPeerCertificate(firstConfig.host, firstConfig.port); + const certificateChain = flattenCertificateChain(certificate); + + expect(certificateChain.length).toEqual(1); + expect(certificateChain[0].subject.CN).toEqual('kibana'); + + await supertest(listener).get('/').expect(200); + + const secondRawConfig = httpConfig.schema.validate({ + name: 'kibana', + host: '127.0.0.1', + port: 10002, + ssl: { + enabled: true, + certificate: ES_CERT_PATH, + key: ES_KEY_PATH, + cipherSuites: ['TLS_AES_256_GCM_SHA384'], + redirectHttpFromPort: 10003, + }, + shutdownTimeout: '1s', + }); + + const secondConfig = new HttpConfig(secondRawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG); + config$.next(secondConfig); + + const secondCertificate = await fetchPeerCertificate(firstConfig.host, firstConfig.port); + const secondCertificateChain = flattenCertificateChain(secondCertificate); + + expect(secondCertificateChain.length).toEqual(1); + expect(secondCertificateChain[0].subject.CN).toEqual('elasticsearch'); + + await supertest(listener).get('/').expect(200); + + await server.stop(); + }); +}); diff --git a/src/core/server/integration_tests/http/tls_utils.ts b/src/core/server/integration_tests/http/tls_utils.ts new file mode 100644 index 0000000000000..e565a61ae5dc6 --- /dev/null +++ b/src/core/server/integration_tests/http/tls_utils.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 { Server as NodeHttpServer } from 'http'; +import { Server as NodeTlsServer } from 'https'; +import tls from 'tls'; + +export function isServerTLS(server: NodeHttpServer): server is NodeTlsServer { + return 'setSecureContext' in server; +} + +export const fetchPeerCertificate = (host: string, port: number) => { + return new Promise((resolve, reject) => { + const socket = tls.connect({ host, port: Number(port), rejectUnauthorized: false }); + socket.once('secureConnect', () => { + const cert = socket.getPeerCertificate(true); + socket.destroy(); + resolve(cert); + }); + socket.once('error', reject); + }); +}; + +export const flattenCertificateChain = ( + cert: tls.DetailedPeerCertificate, + accumulator: tls.DetailedPeerCertificate[] = [] +) => { + accumulator.push(cert); + if (cert.issuerCertificate && cert.fingerprint256 !== cert.issuerCertificate.fingerprint256) { + flattenCertificateChain(cert.issuerCertificate, accumulator); + } + return accumulator; +}; diff --git a/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts b/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts new file mode 100644 index 0000000000000..dc620f87ea55b --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts @@ -0,0 +1,234 @@ +/* + * Copyright 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 { pick } from 'lodash'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { SavedObjectsType, SavedObjectsModelVersionMap } from '@kbn/core-saved-objects-server'; +import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import '../../migrations/jest_matchers'; +import { + getKibanaMigratorTestKit, + startElasticsearch, +} from '../../migrations/kibana_migrator_test_kit'; +import { delay } from '../../migrations/test_utils'; +import { getBaseMigratorParams } from '../../migrations/fixtures/zdt_base.fixtures'; + +export const logFilePath = Path.join(__dirname, 'bulk_update.test.log'); + +describe('SOR - bulk_update API', () => { + let esServer: TestElasticsearchUtils['es']; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + const getType = (version: 'v1' | 'v2'): SavedObjectsType => { + const versionMap: SavedObjectsModelVersionMap = { + 1: { + changes: [], + schemas: { + forwardCompatibility: (attributes) => { + return pick(attributes, 'count'); + }, + }, + }, + }; + + if (version === 'v2') { + versionMap[2] = { + changes: [ + { + type: 'data_backfill', + backfillFn: (document) => { + return { attributes: { even: document.attributes.count % 2 === 0 } }; + }, + }, + ], + }; + } + + return { + name: 'my-test-type', + hidden: false, + namespaceType: 'agnostic', + mappings: { + dynamic: false, + properties: { + count: { type: 'integer' }, + ...(version === 'v2' ? { even: { type: 'boolean' } } : {}), + }, + }, + management: { + importableAndExportable: true, + }, + switchToModelVersionAt: '8.10.0', + modelVersions: versionMap, + }; + }; + + const getOtherType = (version: 'v1' | 'v2'): SavedObjectsType => { + const versionOtherMap: SavedObjectsModelVersionMap = { + 1: { + changes: [], + schemas: { + forwardCompatibility: (attributes) => { + return pick(attributes, 'sum'); + }, + }, + }, + }; + + if (version === 'v2') { + versionOtherMap[2] = { + changes: [ + { + type: 'data_backfill', + backfillFn: (document) => { + return { attributes: { isodd: document.attributes.sum % 2 !== 0 } }; + }, + }, + ], + }; + } + + return { + name: 'my-other-test-type', + hidden: false, + namespaceType: 'agnostic', + mappings: { + dynamic: false, + properties: { + sum: { type: 'integer' }, + ...(version === 'v2' ? { isodd: { type: 'boolean' } } : {}), + }, + }, + management: { + importableAndExportable: true, + }, + switchToModelVersionAt: '8.10.0', + modelVersions: versionOtherMap, + }; + }; + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const setup = async () => { + const { runMigrations: runMigrationV1, savedObjectsRepository: repositoryV1 } = + await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [getType('v1'), getOtherType('v1')], + }); + await runMigrationV1(); + + const { + runMigrations: runMigrationV2, + savedObjectsRepository: repositoryV2, + client: esClient, + } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [getType('v2'), getOtherType('v2')], + }); + await runMigrationV2(); + + return { repositoryV1, repositoryV2, esClient }; + }; + + it('supports updates between older and newer versions', async () => { + const { repositoryV1, repositoryV2, esClient } = await setup(); + + await repositoryV1.create('my-test-type', { count: 12 }, { id: 'my-id' }); + await repositoryV1.create('my-other-test-type', { sum: 24 }, { id: 'my-other-id' }); + + let repoV2Docs = await repositoryV2.bulkGet([ + { type: 'my-test-type', id: 'my-id' }, + { type: 'my-other-test-type', id: 'my-other-id' }, + ]); + const [doc, otherDoc] = repoV2Docs.saved_objects; + + expect(doc.attributes).toEqual({ + count: 12, + even: true, + }); + expect(otherDoc.attributes).toEqual({ + sum: 24, + isodd: false, + }); + + await repositoryV2.bulkUpdate([ + { type: 'my-test-type', id: doc.id, attributes: { count: 11, even: false } }, + // @ts-expect-error cannot assign to partial + { type: 'my-other-test-type', id: otherDoc.id, attributes: { sum: 23, isodd: true } }, + ]); + + const repoV1Docs = await repositoryV1.bulkGet([ + { type: 'my-test-type', id: 'my-id' }, + { type: 'my-other-test-type', id: 'my-other-id' }, + ]); + const [doc1, otherDoc1] = repoV1Docs.saved_objects; + + expect(doc1.attributes).toEqual({ + count: 11, + }); + expect(otherDoc1.attributes).toEqual({ + sum: 23, + }); + + await repositoryV1.bulkUpdate([ + { type: 'my-test-type', id: doc1.id, attributes: { count: 14 } }, + // @ts-expect-error cannot assign to partial + { type: 'my-other-test-type', id: otherDoc1.id, attributes: { sum: 24 } }, + ]); + + repoV2Docs = await repositoryV2.bulkGet([ + { type: 'my-test-type', id: 'my-id' }, + { type: 'my-other-test-type', id: 'my-other-id' }, + ]); + const [doc2, otherDoc2] = repoV2Docs.saved_objects; + + expect(doc2.attributes).toEqual({ + count: 14, + even: true, + }); + expect(otherDoc2.attributes).toEqual({ + sum: 24, + isodd: false, + }); + + const rawDoc = await fetchDoc(esClient, 'my-test-type', 'my-id'); + expect(rawDoc._source).toEqual( + expect.objectContaining({ + typeMigrationVersion: '10.1.0', + 'my-test-type': { + count: 14, + }, + }) + ); + + const otherRawDoc = await fetchDoc(esClient, 'my-other-test-type', 'my-other-id'); + expect(otherRawDoc._source).toEqual( + expect.objectContaining({ + typeMigrationVersion: '10.1.0', + 'my-other-test-type': { + sum: 24, + }, + }) + ); + }); + + const fetchDoc = async (client: ElasticsearchClient, type: string, id: string) => { + return await client.get({ + index: '.kibana', + id: `${type}:${id}`, + }); + }; +}); diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json index 88cac51321322..78219114a51df 100644 --- a/src/core/tsconfig.json +++ b/src/core/tsconfig.json @@ -155,6 +155,8 @@ "@kbn/core-test-helpers-model-versions", "@kbn/core-plugins-contracts-browser", "@kbn/core-plugins-contracts-server", + "@kbn/dev-utils", + "@kbn/server-http-tools", ], "exclude": [ "target/**/*", diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 05b7133519a81..3aa057ac1bd37 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -402,6 +402,7 @@ kibana_vars=( xpack.securitySolution.packagerTaskInterval xpack.securitySolution.prebuiltRulesPackageVersion xpack.spaces.maxSpaces + xpack.task_manager.claim_strategy xpack.task_manager.max_attempts xpack.task_manager.max_workers xpack.task_manager.monitored_aggregated_stats_refresh_rate diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index f9da19183d866..eb7a03a9e933f 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -45,7 +45,7 @@ export async function runDockerGenerator( let imageFlavor = ''; if (flags.baseImage === 'ubi8') imageFlavor += `-ubi8`; - if (flags.baseImage === 'ubi9') imageFlavor += `-ubi9`; + if (flags.baseImage === 'ubi9') imageFlavor += `-ubi`; if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; if (flags.serverless) imageFlavor += '-serverless'; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile index 7fbba7d72ea78..5fe1a6695d06b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/Dockerfile @@ -3,7 +3,7 @@ # Extract Kibana and make various file manipulations. ################################################################################ ARG BASE_REGISTRY=registry1.dso.mil -ARG BASE_IMAGE=redhat/ubi/ubi9 +ARG BASE_IMAGE=ironbank/redhat/ubi/ubi9 ARG BASE_TAG=9.3 FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} as prep_files diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index 24ab98d132203..d36fe4eca01ee 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -42,13 +42,13 @@ const packages: Package[] = [ { // Tip: use `scripts/download_re2.sh` to download binary artifacts from GitHub name: 're2', - version: '1.20.1', + version: '1.20.9', destinationPath: 'node_modules/re2/build/Release/re2.node', extractMethod: 'gunzip', archives: { 'linux-x64': { - url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.1/linux-x64-108.gz', - sha256: 'e14f274f73ede22f170bfe9e57a0645ebf7ed320042a27361fa158bc239a5563', + url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.9/linux-x64-108.gz', + sha256: '136b6cf61b54bf610071a950400518add65d44a4923f11ef658769df1a037f0b', }, // Linux ARM builds are currently done manually as Github Actions used in upstream project // do not natively support an Linux ARM target. @@ -63,20 +63,20 @@ const packages: Package[] = [ // * capture the sha256 with: `shasum -a 256 linux-arm64-*` // * upload the `linux-arm64-*.gz` artifact to the `yarn-prebuilt-artifacts` bucket in GCS using the correct version number 'linux-arm64': { - url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.1/linux-arm64-108.gz', - sha256: 'cbdf3f75a331c601ac0bd34715814d0a1fd17612c6d6b5269f176d46044defd5', + url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.9/linux-arm64-108.gz', + sha256: '311822ac689bd49a534ecf400b4732a288ad218f711b0e593e41dd3a6b739d97', }, 'darwin-x64': { - url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.1/darwin-x64-108.gz', - sha256: 'f88c09e98f152ac15c593b3b923b7fbe28d448cfde5986da40c34461bede5a09', + url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.9/darwin-x64-108.gz', + sha256: '215b6ffb1e5d124439a7dbdd09e4ed1263e065839354a6ad67091ce00d14ee9b', }, 'darwin-arm64': { - url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.1/darwin-arm64-108.gz', - sha256: '80700aecbe63052149aba721449a8ce30c24d884e414025124bb4602efe708be', + url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.9/darwin-arm64-108.gz', + sha256: '0a8bc28150c9efd04f3b52ac214cc7898bde1b8e1f8e6900ae711b03665ff657', }, 'win32-x64': { - url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.1/win32-x64-108.gz', - sha256: 'cadc4713907f3ad1de45f470810ec8e13e08f32c1a1e45e5d5ab5e9d7fcb9763', + url: 'https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.20.9/win32-x64-108.gz', + sha256: '117872144e4a2bb61611aacc51ac9fd24e494c209cf63366f236099a662316eb', }, }, }, diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 1680edb446e36..efd8f22dc0c90 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -85,7 +85,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.1': ['Elastic License 2.0'], - '@elastic/eui@90.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@90.0.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary }; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index ec543a1663d98..c6b425697abcf 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -983,32 +983,33 @@ export default function ({ context.urlTokenPath = ret.urlTokenPath; const components = getTopLevelUrlCompleteComponents(context.method); - const { tokenPath, predicate } = (() => { - const lastUrlTokenPath = - Array.isArray(context.urlTokenPath) && context.urlTokenPath.length !== 0 - ? context.urlTokenPath[context.urlTokenPath.length - 1] - : null; - // Checking the last chunk of path like 'c,d,' of 'GET /a/b/c,d,' - if ( - Array.isArray(lastUrlTokenPath) && - // true if neither c nor d equals to every ConstantComponent's name (such as _search) - !_.find( - components, - (c) => c instanceof ConstantComponent && _.find(lastUrlTokenPath, (p) => c.name === p) - ) - ) { - // will simulate autocomplete on 'GET /a/b/' with a filter by index - return { - tokenPath: context.urlTokenPath?.slice(0, -1), - predicate: (term: ReturnType[0]) => term.meta === 'index', - }; - } else { - // will do nothing special - return { tokenPath: context.urlTokenPath, predicate: () => true }; + let urlTokenPath = context.urlTokenPath; + let predicate: (term: ReturnType[0]) => boolean = () => true; + + const tokenIter = createTokenIterator({ editor, position: pos }); + const currentTokenType = tokenIter.getCurrentToken()?.type; + const previousTokenType = tokenIter.stepBackward()?.type; + if (!Array.isArray(urlTokenPath)) { + // skip checks for url.comma + } else if (previousTokenType === 'url.comma' && currentTokenType === 'url.comma') { + predicate = () => false; // two consecutive commas empty the autocomplete + } else if ( + (previousTokenType === 'url.part' && currentTokenType === 'url.comma') || + (previousTokenType === 'url.slash' && currentTokenType === 'url.comma') || + (previousTokenType === 'url.comma' && currentTokenType === 'url.part') + ) { + const lastUrlTokenPath = _.last(urlTokenPath) || []; // ['c', 'd'] from 'GET /a/b/c,d,' + const constantComponents = _.filter(components, (c) => c instanceof ConstantComponent); + const constantComponentNames = _.map(constantComponents, 'name'); + + // check if neither 'c' nor 'd' is a constant component name such as '_search' + if (_.every(lastUrlTokenPath, (token) => !_.includes(constantComponentNames, token))) { + urlTokenPath = urlTokenPath.slice(0, -1); // drop the last 'c,d,' part from the url path + predicate = (term) => term.meta === 'index'; // limit the autocomplete to indices only } - })(); + } - populateContext(tokenPath, context, editor, true, components); + populateContext(urlTokenPath, context, editor, true, components); context.autoCompleteSet = _.filter( addMetaToTermsList(context.autoCompleteSet!, 'endpoint'), predicate diff --git a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts index 969f06f8d994f..cbcfb724798df 100644 --- a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts +++ b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.test.ts @@ -103,6 +103,10 @@ describe('looksLikeTypingIn', () => { { preamble: 'GET _cat/indices?s=index&exp', autocomplete: 'and_wildcards', input: '=' }, { preamble: 'GET _cat/indices?v&', autocomplete: 'expand_wildcards', input: '=' }, { preamble: 'GET _cat/indices?v&exp', autocomplete: 'and_wildcards', input: '=' }, + // autocomplete skips one iteration of token evaluation if user types in every letter + { preamble: 'GET .kibana', autocomplete: '/', input: '_' }, // token '/' may not be evaluated + { preamble: 'GET .kibana', autocomplete: ',', input: '.' }, // token ',' may not be evaluated + { preamble: 'GET .kibana', autocomplete: '?', input: 'k' }, // token '?' may not be evaluated ]; for (const c of cases) { const name = diff --git a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts index a679aa2eda117..44dbb99025c73 100644 --- a/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts +++ b/src/plugins/console/public/lib/autocomplete/looks_like_typing_in.ts @@ -11,6 +11,7 @@ import type { CoreEditor, Position, Token } from '../../types'; enum Move { ForwardOneCharacter = 1, ForwardOneToken, // the column position may jump to the next token by autocomplete + ForwardTwoTokens, // the column position could jump two tokens due to autocomplete } const knownTypingInTokenTypes = new Map>>([ @@ -43,6 +44,10 @@ const knownTypingInTokenTypes = new Map>>([ ['whitespace', new Set(['url.comma', 'url.questionmark', 'url.slash'])], ]), ], + [ + Move.ForwardTwoTokens, + new Map>([['url.part', new Set(['url.param', 'url.part'])]]), + ], ]); const getOneCharacterNextOnTheRight = (pos: Position, coreEditor: CoreEditor): string => { @@ -75,14 +80,16 @@ export const looksLikeTypingIn = ( currentToken.value.length === 1 && getOneCharacterNextOnTheRight(currentToken.position, coreEditor) === '' ) { - const move = + const moves = lastEvaluatedToken.position.column + 1 === currentToken.position.column - ? Move.ForwardOneCharacter - : Move.ForwardOneToken; - const tokenTypesPairs = knownTypingInTokenTypes.get(move) ?? new Map>(); - const currentTokenTypes = tokenTypesPairs.get(lastEvaluatedToken.type) ?? new Set(); - if (currentTokenTypes.has(currentToken.type)) { - return true; + ? [Move.ForwardOneCharacter] + : [Move.ForwardOneToken, Move.ForwardTwoTokens]; + for (const move of moves) { + const tokenTypesPairs = knownTypingInTokenTypes.get(move) ?? new Map>(); + const currentTokenTypes = tokenTypesPairs.get(lastEvaluatedToken.type) ?? new Set(); + if (currentTokenTypes.has(currentToken.type)) { + return true; + } } } 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 0190bbaefa00b..6afdf1429663b 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 @@ -54,12 +54,14 @@ export function DashboardEditingToolbar({ isDisabled }: { isDisabled?: boolean } trackUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); } - if ('aliasPath' in visType) { - appId = visType.aliasApp; - path = visType.aliasPath; - } else { + if (!('alias' in visType)) { + // this visualization is not an alias appId = 'visualize'; path = `#/create?type=${encodeURIComponent(visType.name)}`; + } else if (visType.alias && 'path' in visType.alias) { + // this visualization **is** an alias, and it has an app to redirect to for creation + appId = visType.alias.app; + path = visType.alias.path; } } else { appId = 'visualize'; 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 d2b6470650caa..18f26704221ab 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 @@ -104,10 +104,11 @@ export const EditorMenu = ({ createNewVisType, createNewEmbeddable, isDisabled } const promotedVisTypes = getSortedVisTypesByGroup(VisGroups.PROMOTED); const aggsBasedVisTypes = getSortedVisTypesByGroup(VisGroups.AGGBASED); const toolVisTypes = getSortedVisTypesByGroup(VisGroups.TOOLS); - const visTypeAliases = getVisTypeAliases().sort( - ({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => + const visTypeAliases = getVisTypeAliases() + .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => a === b ? 0 : a ? -1 : 1 - ); + ) + .filter(({ disableCreate }: VisTypeAlias) => !disableCreate); const factories = unwrappedEmbeddableFactories.filter( ({ isEditable, factory: { type, canCreateNew, isContainerType } }) => diff --git a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx index 8767b5abe3567..de7d79d456b9f 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx @@ -10,21 +10,21 @@ import React, { useCallback, useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { - EuiText, - EuiImage, EuiButton, - EuiFlexItem, - EuiFlexGroup, EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiImage, EuiPageTemplate, + EuiText, } from '@elastic/eui'; import { METRIC_TYPE } from '@kbn/analytics'; import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { DASHBOARD_UI_METRIC_ID } from '../../../dashboard_constants'; import { pluginServices } from '../../../services/plugin_services'; -import { emptyScreenStrings } from '../../_dashboard_container_strings'; import { useDashboardContainer } from '../../embeddable/dashboard_container'; -import { DASHBOARD_UI_METRIC_ID } from '../../../dashboard_constants'; +import { emptyScreenStrings } from '../../_dashboard_container_strings'; export function DashboardEmptyScreen() { const { @@ -53,7 +53,7 @@ export function DashboardEmptyScreen() { const originatingApp = embeddableAppContext?.currentAppId; const goToLens = useCallback(() => { - if (!lensAlias || !lensAlias.aliasPath) return; + if (!lensAlias || !lensAlias.alias) return; const trackUiMetric = usageCollection.reportUiCounter?.bind( usageCollection, DASHBOARD_UI_METRIC_ID @@ -62,8 +62,8 @@ export function DashboardEmptyScreen() { if (trackUiMetric) { trackUiMetric(METRIC_TYPE.CLICK, `${lensAlias.name}:create`); } - getStateTransfer().navigateToEditor(lensAlias.aliasApp, { - path: lensAlias.aliasPath, + getStateTransfer().navigateToEditor(lensAlias.alias.app, { + path: lensAlias.alias.path, state: { originatingApp, originatingPath, diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx index 9eb12379741ab..083d47ee4e69e 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx @@ -108,6 +108,7 @@ export const Item = React.forwardRef( key={type} index={index} showBadges={true} + showShadow={true} showNotifications={true} onPanelStatusChange={onPanelStatusChange} embeddable={() => container.untilEmbeddableLoaded(id)} diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index ec30a92bae996..ca6096c8160df 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -31,7 +31,10 @@ type Output = Observable; interface Arguments { query: string; - timezone?: string; + // TODO: time_zone support was temporarily removed from ES|QL, + // we will need to add it back in once it is supported again. + // https://github.com/elastic/elasticsearch/pull/102767 + // timezone?: string; timeField?: string; locale?: string; } @@ -67,10 +70,6 @@ function normalizeType(type: string): DatatableColumnType { } } -function sanitize(value: string) { - return value.replace(/[\(\)]/g, '_'); -} - function extractTypeAndReason(attributes: any): { type?: string; reason?: string } { if (['type', 'reason'].every((prop) => prop in attributes)) { return attributes; @@ -82,7 +81,10 @@ function extractTypeAndReason(attributes: any): { type?: string; reason?: string } interface ESQLSearchParams { - time_zone?: string; + // TODO: time_zone support was temporarily removed from ES|QL, + // we will need to add it back in once it is supported again. + // https://github.com/elastic/elasticsearch/pull/102767 + // time_zone?: string; query: string; filter?: unknown; locale?: string; @@ -112,15 +114,15 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { defaultMessage: 'An ES|QL query.', }), }, - timezone: { - aliases: ['tz'], - types: ['string'], - default: 'UTC', - help: i18n.translate('data.search.esql.timezone.help', { - defaultMessage: - 'The timezone to use for date operations. Valid ISO8601 formats and UTC offsets both work.', - }), - }, + // timezone: { + // aliases: ['tz'], + // types: ['string'], + // default: 'UTC', + // help: i18n.translate('data.search.esql.timezone.help', { + // defaultMessage: + // 'The timezone to use for date operations. Valid ISO8601 formats and UTC offsets both work.', + // }), + // }, timeField: { aliases: ['timeField'], types: ['string'], @@ -138,7 +140,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { }, fn( input, - { query, timezone, timeField, locale }, + { query, /* timezone, */ timeField, locale }, { abortSignal, inspectorAdapters, getKibanaRequest } ) { return defer(() => @@ -157,7 +159,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { switchMap(({ search, uiSettings }) => { const params: ESQLSearchParams = { query, - time_zone: timezone, + // time_zone: timezone, locale, }; if (input) { @@ -249,8 +251,8 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { map(({ rawResponse: body, warning }) => { const columns = body.columns?.map(({ name, type }) => ({ - id: sanitize(name), - name: sanitize(name), + id: name, + name, meta: { type: normalizeType(type) }, })) ?? []; const columnNames = columns.map(({ name }) => name); diff --git a/src/plugins/data/server/search/session/session_service.test.ts b/src/plugins/data/server/search/session/session_service.test.ts index 3f79049fa9666..481fabcfa423e 100644 --- a/src/plugins/data/server/search/session/session_service.test.ts +++ b/src/plugins/data/server/search/session/session_service.test.ts @@ -18,7 +18,7 @@ import { createRequestHash } from './utils'; import moment from 'moment'; import { coreMock } from '@kbn/core/server/mocks'; import { ConfigSchema } from '../../../config'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../common'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; diff --git a/src/plugins/data_views/docs/openapi/bundled.json b/src/plugins/data_views/docs/openapi/bundled.json index d661e0a7cbaad..e4f7e9856e2bd 100644 --- a/src/plugins/data_views/docs/openapi/bundled.json +++ b/src/plugins/data_views/docs/openapi/bundled.json @@ -740,7 +740,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "examples": { diff --git a/src/plugins/data_views/docs/openapi/bundled.yaml b/src/plugins/data_views/docs/openapi/bundled.yaml index c6e52614b66e9..65ffd986ade7d 100644 --- a/src/plugins/data_views/docs/openapi/bundled.yaml +++ b/src/plugins/data_views/docs/openapi/bundled.yaml @@ -474,7 +474,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' examples: get_data_views_response: summary: The get all data views API returns a list of data views. diff --git a/src/plugins/data_views/docs/openapi/entrypoint.yaml b/src/plugins/data_views/docs/openapi/entrypoint.yaml index 991d0e32154ae..697fd554dae8c 100644 --- a/src/plugins/data_views/docs/openapi/entrypoint.yaml +++ b/src/plugins/data_views/docs/openapi/entrypoint.yaml @@ -52,7 +52,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - apiKeyAuth: [] diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx index 19cf02b43585c..421792ca8cb63 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx @@ -195,11 +195,13 @@ export function DiscoverGridFlyout({ }); const flyoutTitle = flyoutCustomization?.title ?? defaultFlyoutTitle; + const flyoutSize = flyoutCustomization?.size ?? 'm'; + return ( { @@ -162,6 +162,38 @@ describe('getSharingData', () => { ]); }); + test('getSearchSource supports nested fields', async () => { + const index = buildDataViewMock({ + name: 'the-data-view', + timeFieldName: 'cool-timefield', + fields: [ + ...dataViewMock.fields, + { + name: 'cool-field-2.field', + type: 'keyword', + subType: { + nested: { + path: 'cool-field-2.field.path', + }, + }, + }, + ] as DataView['fields'], + }); + const searchSourceMock = createSearchSourceMock({ index }); + const { getSearchSource } = await getSharingData( + searchSourceMock, + { + columns: ['cool-field-1', 'cool-field-2'], + }, + services + ); + expect(getSearchSource({}).fields).toStrictEqual([ + { field: 'cool-timefield', include_unmapped: 'true' }, + { field: 'cool-field-1', include_unmapped: 'true' }, + { field: 'cool-field-2.*', include_unmapped: 'true' }, + ]); + }); + test('fields have prepended timeField', async () => { const index = { ...dataViewMock } as DataView; index.timeFieldName = 'cool-timefield'; diff --git a/src/plugins/discover/public/utils/get_sharing_data.ts b/src/plugins/discover/public/utils/get_sharing_data.ts index 0e243385272c4..9e3b2b2369469 100644 --- a/src/plugins/discover/public/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/utils/get_sharing_data.ts @@ -17,6 +17,7 @@ import type { Filter } from '@kbn/es-query'; import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { DOC_HIDE_TIME_COLUMN_SETTING, + isNestedFieldParent, SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, } from '@kbn/discover-utils'; @@ -113,7 +114,17 @@ export async function getSharingData( if (useFieldsApi) { searchSource.removeField('fieldsFromSource'); const fields = columns.length - ? columns.map((field) => ({ field, include_unmapped: 'true' })) + ? columns.map((column) => { + let field = column; + + // If this column is a nested field, add a wildcard to the field name in order to fetch + // all leaf fields for the report, since the fields API doesn't support nested field roots + if (isNestedFieldParent(column, index)) { + field = `${column}.*`; + } + + return { field, include_unmapped: 'true' }; + }) : [{ field: '*', include_unmapped: 'true' }]; searchSource.setField('fields', fields); diff --git a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx index 93279e311b065..2c9a30431218d 100644 --- a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx @@ -12,9 +12,8 @@ import { distinct, map } from 'rxjs'; import React, { ReactNode, useEffect, useMemo, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, htmlIdGenerator } from '@elastic/eui'; -import { isPromise } from '@kbn/std'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; - +import { PanelLoader } from '@kbn/panel-loader'; import { EditPanelAction, RemovePanelAction, @@ -51,6 +50,7 @@ const getEventStatus = (output: EmbeddableOutput): EmbeddablePhase => { export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { const { hideHeader, showShadow, embeddable, hideInspector, onPanelStatusChange } = panelProps; const [node, setNode] = useState(); + const [initialLoadComplete, setInitialLoadComplete] = useState(false); const embeddableRoot: React.RefObject = useMemo(() => React.createRef(), []); const headerId = useMemo(() => htmlIdGenerator()(), []); @@ -129,6 +129,11 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { * Select state from the embeddable */ const loading = useSelectFromEmbeddableOutput('loading', embeddable); + + if (loading === false && !initialLoadComplete) { + setInitialLoadComplete(true); + } + const viewMode = useSelectFromEmbeddableInput('viewMode', embeddable); /** @@ -136,21 +141,30 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { */ useEffect(() => { if (!embeddableRoot.current) return; - const nextNode = embeddable.render(embeddableRoot.current) ?? undefined; - if (isPromise(nextNode)) { - nextNode.then((resolved) => setNode(resolved)); - } else { + + let cancelled = false; + + const render = async (root: HTMLDivElement) => { + const nextNode = (await embeddable.render(root)) ?? undefined; + + if (cancelled) return; + setNode(nextNode); - } + }; + + render(embeddableRoot.current); + const errorSubscription = embeddable.getOutput$().subscribe({ next: (output) => { setOutputError(output.error); }, error: (error) => setOutputError(error), }); + return () => { embeddable?.destroy(); errorSubscription?.unsubscribe(); + cancelled = true; }; }, [embeddable, embeddableRoot]); @@ -207,7 +221,13 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { )} -
    + {!initialLoadComplete && } +
    {node}
    diff --git a/src/plugins/embeddable/public/embeddable_panel/index.tsx b/src/plugins/embeddable/public/embeddable_panel/index.tsx index 9e29d49cbfd5b..7c8c87d255981 100644 --- a/src/plugins/embeddable/public/embeddable_panel/index.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/index.tsx @@ -8,16 +8,19 @@ import React from 'react'; +import { PanelLoader } from '@kbn/panel-loader'; import { EmbeddablePanelProps } from './types'; import { useEmbeddablePanel } from './use_embeddable_panel'; -import { EmbeddableLoadingIndicator } from './embeddable_loading_indicator'; /** * Loads and renders an embeddable. */ export const EmbeddablePanel = (props: EmbeddablePanelProps) => { const result = useEmbeddablePanel({ embeddable: props.embeddable }); - if (!result) return ; + if (!result) + return ( + + ); const { embeddable, ...passThroughProps } = props; return ; }; diff --git a/src/plugins/embeddable/tsconfig.json b/src/plugins/embeddable/tsconfig.json index 9055c59c2d846..8239fba0d0d18 100644 --- a/src/plugins/embeddable/tsconfig.json +++ b/src/plugins/embeddable/tsconfig.json @@ -34,6 +34,7 @@ "@kbn/react-kibana-mount", "@kbn/unified-search-plugin", "@kbn/data-views-plugin", + "@kbn/panel-loader", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx index 1d479bd9b4c1c..43723ecc06984 100644 --- a/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer/react_expression_renderer.tsx @@ -8,7 +8,8 @@ import React, { useRef } from 'react'; import classNames from 'classnames'; -import { EuiLoadingChart, EuiProgress, useEuiTheme } from '@elastic/eui'; +import { PanelLoader } from '@kbn/panel-loader'; +import { EuiProgress, useEuiTheme } from '@elastic/eui'; import { ExpressionRenderError } from '../types'; import type { ExpressionRendererParams } from './use_expression_renderer'; import { useExpressionRenderer } from './use_expression_renderer'; @@ -56,7 +57,7 @@ export function ReactExpressionRenderer({ return (
    - {isEmpty && } + {isEmpty && } {isLoading && ( )} diff --git a/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx b/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx index fe0bfcc42d602..57e2951c12443 100644 --- a/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx +++ b/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx @@ -7,7 +7,7 @@ */ import React, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { PanelLoader } from '@kbn/panel-loader'; import type { ReactExpressionRendererProps } from './react_expression_renderer'; const ReactExpressionRendererComponent = lazy(async () => { @@ -17,7 +17,7 @@ const ReactExpressionRendererComponent = lazy(async () => { }); export const ReactExpressionRenderer = (props: ReactExpressionRendererProps) => ( - }> + }> ); diff --git a/src/plugins/expressions/tsconfig.json b/src/plugins/expressions/tsconfig.json index 1f1128ae6ab19..94f1d7852da39 100644 --- a/src/plugins/expressions/tsconfig.json +++ b/src/plugins/expressions/tsconfig.json @@ -16,6 +16,7 @@ "@kbn/std", "@kbn/core-execution-context-common", "@kbn/tinymath", + "@kbn/panel-loader", ], "exclude": [ "target/**/*", diff --git a/src/plugins/image_embeddable/public/image_editor/configure_image.tsx b/src/plugins/image_embeddable/public/image_editor/configure_image.tsx index 416a35f61870f..357f3ff46785a 100644 --- a/src/plugins/image_embeddable/public/image_editor/configure_image.tsx +++ b/src/plugins/image_embeddable/public/image_editor/configure_image.tsx @@ -11,7 +11,7 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { FilesContext } from '@kbn/shared-ux-file-context'; import { skip, take, takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { ImageConfig } from '../types'; import { ImageEditorFlyout } from './image_editor_flyout'; import { ImageViewerContext } from '../image_viewer'; diff --git a/src/plugins/image_embeddable/public/image_editor/image_editor_flyout.tsx b/src/plugins/image_embeddable/public/image_editor/image_editor_flyout.tsx index 7954184384920..c8d5f9b9636fc 100644 --- a/src/plugins/image_embeddable/public/image_editor/image_editor_flyout.tsx +++ b/src/plugins/image_embeddable/public/image_editor/image_editor_flyout.tsx @@ -34,7 +34,7 @@ import { FileUpload } from '@kbn/shared-ux-file-upload'; import { FilePicker } from '@kbn/shared-ux-file-picker'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { FileImageMetadata, imageEmbeddableFileKind } from '../imports'; import { ImageConfig } from '../types'; import { ImageViewer } from '../image_viewer/image_viewer'; // use eager version to avoid flickering diff --git a/src/plugins/image_embeddable/public/image_embeddable/image_embeddable_factory.tsx b/src/plugins/image_embeddable/public/image_embeddable/image_embeddable_factory.tsx index ae2b2a4406fa6..c824b7b50765d 100644 --- a/src/plugins/image_embeddable/public/image_embeddable/image_embeddable_factory.tsx +++ b/src/plugins/image_embeddable/public/image_embeddable/image_embeddable_factory.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { IExternalUrl } from '@kbn/core-http-browser'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { IContainer, EmbeddableInput, diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/__snapshots__/clusters_view.test.tsx.snap b/src/plugins/inspector/public/views/requests/components/details/clusters_view/__snapshots__/clusters_view.test.tsx.snap deleted file mode 100644 index 0186e1c713430..0000000000000 --- a/src/plugins/inspector/public/views/requests/components/details/clusters_view/__snapshots__/clusters_view.test.tsx.snap +++ /dev/null @@ -1,96 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`render should render local and remote cluster details from _clusters 1`] = ` - - - - - -`; - -exports[`render should render local cluster details from _shards 1`] = ` - - - - -`; diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_health/cluster_health.tsx b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_health/cluster_health.tsx index 4e4e57f5284c7..af066c3fac713 100644 --- a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_health/cluster_health.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_health/cluster_health.tsx @@ -8,15 +8,21 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiHealth, EuiText } from '@elastic/eui'; +import { EuiHealth, EuiText, EuiTextProps } from '@elastic/eui'; import { HEALTH_HEX_CODES } from './gradient'; interface Props { count?: number; status: string; + textProps?: EuiTextProps; } -export function ClusterHealth({ count, status }: Props) { +const defaultTextProps: EuiTextProps = { + size: 'xs', + color: 'subdued', +}; + +export function ClusterHealth({ count, status, textProps = defaultTextProps }: Props) { if (typeof count === 'number' && count === 0) { return null; } @@ -48,9 +54,7 @@ export function ClusterHealth({ count, status }: Props) { const label = typeof count === 'number' ? `${count} ${statusLabel}` : statusLabel; return ( - - {label} - + {label} ); } diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.test.tsx b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.test.tsx new file mode 100644 index 0000000000000..19c269886041c --- /dev/null +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { ClusterDetails } from '@kbn/es-types'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { ClustersTable } from './clusters_table'; + +describe('ClustersTable', () => { + describe('sorting', () => { + const clusters = { + remote1: { + status: 'successful', + took: 50, + } as unknown as ClusterDetails, + remote2: { + status: 'skipped', + took: 1000, + } as unknown as ClusterDetails, + remote3: { + status: 'failed', + took: 90, + } as unknown as ClusterDetails, + }; + + test('should render rows in native order', () => { + render(); + const tableRows = screen.getAllByRole('row'); + expect(tableRows.length).toBe(4); // 1 header row, 3 data rows + expect(tableRows[1]).toHaveTextContent('Nameremote1StatussuccessfulResponse time50ms'); + expect(tableRows[2]).toHaveTextContent('Nameremote2StatusskippedResponse time1000ms'); + expect(tableRows[3]).toHaveTextContent('Nameremote3StatusfailedResponse time90ms'); + }); + + test('should sort by response time', () => { + render(); + const button = screen.getByRole('button', { + name: 'Response time', + }); + fireEvent.click(button); + const tableRowsAsc = screen.getAllByRole('row'); + expect(tableRowsAsc.length).toBe(4); // 1 header row, 3 data rows + expect(tableRowsAsc[1]).toHaveTextContent('Nameremote1StatussuccessfulResponse time50ms'); + expect(tableRowsAsc[2]).toHaveTextContent('Nameremote3StatusfailedResponse time90ms'); + expect(tableRowsAsc[3]).toHaveTextContent('Nameremote2StatusskippedResponse time1000ms'); + + fireEvent.click(button); + const tableRowsDesc = screen.getAllByRole('row'); + expect(tableRowsDesc.length).toBe(4); // 1 header row, 3 data rows + expect(tableRowsDesc[1]).toHaveTextContent('Nameremote2StatusskippedResponse time1000ms'); + expect(tableRowsDesc[2]).toHaveTextContent('Nameremote3StatusfailedResponse time90ms'); + expect(tableRowsDesc[3]).toHaveTextContent('Nameremote1StatussuccessfulResponse time50ms'); + }); + }); +}); diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.tsx b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.tsx index c9fea1a470f49..03de4a0b999f4 100644 --- a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_table/clusters_table.tsx @@ -6,10 +6,17 @@ * Side Public License, v 1. */ -import React, { useState, ReactNode } from 'react'; +import React, { useMemo, useState, ReactNode } from 'react'; import type { ClusterDetails } from '@kbn/es-types'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, type EuiBasicTableColumn, EuiButtonIcon, EuiText } from '@elastic/eui'; +import { + Comparators, + EuiBasicTable, + type EuiBasicTableColumn, + EuiButtonIcon, + EuiText, + Criteria, +} from '@elastic/eui'; import { ClusterView } from './cluster_view'; import { ClusterHealth } from '../clusters_health'; import { LOCAL_CLUSTER_KEY } from '../local_cluster'; @@ -21,7 +28,7 @@ function getInitialExpandedRow(clusters: Record) { : {}; } -interface ClusterColumn { +interface ClusterItem { name: string; status: string; responseTime?: number; @@ -35,6 +42,8 @@ export function ClustersTable({ clusters }: Props) { const [expandedRows, setExpandedRows] = useState>( getInitialExpandedRow(clusters) ); + const [sortField, setSortField] = useState(); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); const toggleDetails = (name: string) => { const nextExpandedRows = { ...expandedRows }; @@ -46,7 +55,17 @@ export function ClustersTable({ clusters }: Props) { setExpandedRows(nextExpandedRows); }; - const columns: Array> = [ + const items = useMemo(() => { + return Object.keys(clusters).map((key) => { + return { + name: key, + status: clusters[key].status, + responseTime: clusters[key].took, + }; + }); + }, [clusters]); + + const columns: Array> = [ { field: 'name', name: i18n.translate('inspector.requests.clusters.table.nameLabel', { @@ -78,6 +97,7 @@ export function ClustersTable({ clusters }: Props) { ); }, + sortable: items.length > 1, width: '60%', }, { @@ -88,6 +108,7 @@ export function ClustersTable({ clusters }: Props) { render: (status: string) => { return ; }, + sortable: items.length > 1, }, { align: 'right' as 'right', @@ -105,22 +126,38 @@ export function ClustersTable({ clusters }: Props) { : null} ), + sortable: items.length > 1, }, ]; return ( { - return { - name: key, - status: clusters[key].status, - responseTime: clusters[key].took, - }; - })} + items={ + sortField + ? items.sort(Comparators.property(sortField, Comparators.default(sortDirection))) + : items + } isExpandable={true} itemIdToExpandedRowMap={expandedRows} itemId="name" columns={columns} + sorting={{ + sort: sortField + ? { + field: sortField, + direction: sortDirection, + } + : undefined, + }} + onChange={({ sort }: Criteria) => { + if (sort) { + setSortField(sort.field); + setSortDirection(sort.direction); + } + }} + noItemsMessage={i18n.translate('inspector.requests.clusters.table.noItemsFound', { + defaultMessage: 'No clusters found', + })} /> ); } diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.test.tsx b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.test.tsx index 971c3bad5bef8..7fd1edf1ecc0b 100644 --- a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.test.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { render, screen, fireEvent } from '@testing-library/react'; import { ClustersView } from './clusters_view'; import { Request } from '../../../../../../common/adapters/request/types'; @@ -51,26 +51,33 @@ describe('shouldShow', () => { }); describe('render', () => { - test('should render local cluster details from _shards', () => { - const request = { - response: { - json: { - rawResponse: { - _shards: { - total: 2, - successful: 2, - skipped: 0, - failed: 0, + describe('single cluster', () => { + test('should display table and not display search bar and health bar', () => { + const request = { + response: { + json: { + rawResponse: { + _shards: { + total: 2, + successful: 2, + skipped: 0, + failed: 0, + }, }, }, }, - }, - } as unknown as Request; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + } as unknown as Request; + render(); + const table = screen.getByRole('table'); + expect(table).not.toBeNull(); + const searchbar = screen.queryByRole('searchbox'); + expect(searchbar).toBeNull(); + const healthbar = screen.queryByText('2 clusters'); + expect(healthbar).toBeNull(); + }); }); - test('should render local and remote cluster details from _clusters', () => { + describe('multiple clusters', () => { const request = { response: { json: { @@ -110,7 +117,35 @@ describe('render', () => { }, }, } as unknown as Request; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + + test('should display table, search bar, and health bar', () => { + render(); + const table = screen.getByRole('table'); + expect(table).not.toBeNull(); + const searchbar = screen.getByRole('searchbox'); + expect(searchbar).not.toBeNull(); + const healthbar = screen.getByText('2 clusters'); + expect(healthbar).not.toBeNull(); + }); + + test('should filter table and health bar', () => { + render(); + const searchbar = screen.getByRole('searchbox'); + fireEvent.change(searchbar, { target: { value: 'remot' } }); + const tableRows = screen.getAllByRole('row'); + expect(tableRows.length).toBe(2); // table header and matching table row + const healthbar = screen.getByText('1 cluster'); + expect(healthbar).not.toBeNull(); + }); + + test('should display search bar when there are no matches for search', () => { + render(); + const searchbar = screen.getByRole('searchbox'); + fireEvent.change(searchbar, { target: { value: 'nevergonafindme' } }); + const notFoundRow = screen.getByRole('row', { name: 'No clusters found' }); + expect(notFoundRow).not.toBeNull(); + const searchbarAfterSearch = screen.getByRole('searchbox'); + expect(searchbarAfterSearch).not.toBeNull(); + }); }); }); diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.tsx b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.tsx index 8a96847832dc8..c14822d93561a 100644 --- a/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/clusters_view.tsx @@ -8,45 +8,100 @@ import React, { Component } from 'react'; import { estypes } from '@elastic/elasticsearch'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiSearchBar, type EuiSearchBarOnChangeArgs, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import type { ClusterDetails } from '@kbn/es-types'; import { Request } from '../../../../../../common/adapters/request/types'; import type { DetailViewProps } from '../types'; -import { getLocalClusterDetails, LOCAL_CLUSTER_KEY } from './local_cluster'; -import { ClustersHealth } from './clusters_health'; +import { ClusterHealth, ClustersHealth } from './clusters_health'; import { ClustersTable } from './clusters_table'; +import { findClusters } from './find_clusters'; -export class ClustersView extends Component { +interface State { + clusters: Record; + showSearchAndStatusBar: boolean; +} + +export class ClustersView extends Component { static shouldShow = (request: Request) => Boolean( (request.response?.json as { rawResponse?: estypes.SearchResponse })?.rawResponse?._shards || (request.response?.json as { rawResponse?: estypes.SearchResponse })?.rawResponse?._clusters ); - render() { - const rawResponse = ( - this.props.request.response?.json as { rawResponse?: estypes.SearchResponse } - )?.rawResponse; - if (!rawResponse) { - return null; - } + constructor(props: DetailViewProps) { + super(props); + const clusters = findClusters(this.props.request); + this.state = { + clusters, + showSearchAndStatusBar: Object.keys(clusters).length > 1, + }; + } - const clusters = rawResponse._clusters - ? ( - rawResponse._clusters as estypes.ClusterStatistics & { - details: Record; - } - ).details - : { - [LOCAL_CLUSTER_KEY]: getLocalClusterDetails(rawResponse), - }; + _onSearchChange = ({ query, error }: EuiSearchBarOnChangeArgs) => { + if (!error) { + this.setState({ clusters: findClusters(this.props.request, query) }); + } + }; - return this.props.request.response?.json ? ( + render() { + return ( <> - {Object.keys(clusters).length > 1 ? : null} - + {this.state.showSearchAndStatusBar ? ( + <> + + ), + }, + { + value: 'partial', + view: ( + + ), + }, + { + value: 'skipped', + view: ( + + ), + }, + { + value: 'failed', + view: ( + + ), + }, + ], + }, + ]} + onChange={this._onSearchChange} + /> + + + + ) : null} + - ) : null; + ); } } diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.test.ts b/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.test.ts new file mode 100644 index 0000000000000..79cc378d89128 --- /dev/null +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EuiSearchBar } from '@elastic/eui'; +import { findClusters } from './find_clusters'; +import { LOCAL_CLUSTER_KEY } from './local_cluster'; +import { Request } from '../../../../../../common/adapters/request/types'; + +const request = { + response: { + json: { + rawResponse: { + _clusters: { + details: { + [LOCAL_CLUSTER_KEY]: { + status: 'successful', + took: 50, + }, + remote1: { + status: 'skipped', + took: 1000, + }, + remote2: { + status: 'failed', + took: 90, + }, + }, + }, + }, + }, + }, +} as unknown as Request; + +describe('findClusters', () => { + test('should return all clusters when query is not provided', () => { + const clusters = findClusters(request); + expect(Object.keys(clusters)).toEqual([LOCAL_CLUSTER_KEY, 'remote1', 'remote2']); + }); + + test('should filter clusters by cluster name', () => { + const clusters = findClusters(request, EuiSearchBar.Query.parse('remo')); + expect(Object.keys(clusters)).toEqual(['remote1', 'remote2']); + }); + + test('should filter clusters by cluster name and status', () => { + const clusters = findClusters( + request, + EuiSearchBar.Query.parse('remo status:(successful or skipped)') + ); + expect(Object.keys(clusters)).toEqual(['remote1']); + }); + + test('should filter by multiple status values', () => { + const clusters = findClusters( + request, + EuiSearchBar.Query.parse('status:(successful or skipped)') + ); + expect(Object.keys(clusters)).toEqual([LOCAL_CLUSTER_KEY, 'remote1']); + }); +}); diff --git a/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.ts b/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.ts new file mode 100644 index 0000000000000..8b62cf20d1b48 --- /dev/null +++ b/src/plugins/inspector/public/views/requests/components/details/clusters_view/find_clusters.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 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 { estypes } from '@elastic/elasticsearch'; +import type { ClusterDetails } from '@kbn/es-types'; +import { EuiSearchBar, type Query } from '@elastic/eui'; +import { Request } from '../../../../../../common/adapters/request/types'; +import { getLocalClusterDetails, LOCAL_CLUSTER_KEY } from './local_cluster'; + +export function findClusters(request: Request, query?: Query): Record { + const rawResponse = (request.response?.json as { rawResponse?: estypes.SearchResponse }) + ?.rawResponse; + if (!rawResponse) { + return {}; + } + + const clusters = rawResponse._clusters + ? ( + rawResponse._clusters as estypes.ClusterStatistics & { + details: Record; + } + ).details + : { + [LOCAL_CLUSTER_KEY]: getLocalClusterDetails(rawResponse), + }; + + if (!query) { + return clusters; + } + + const clusterItems = Object.keys(clusters).map((key) => { + return { + name: key, + status: clusters[key].status, + }; + }); + + const narrowedClusterItems = EuiSearchBar.Query.execute(query, clusterItems, { + defaultFields: ['name'], + }); + + const narrowedClusers: Record = {}; + narrowedClusterItems.forEach(({ name }) => { + narrowedClusers[name] = clusters[name]; + }); + return narrowedClusers; +} diff --git a/src/plugins/inspector/public/views/requests/components/request_details.tsx b/src/plugins/inspector/public/views/requests/components/request_details.tsx index 7677cca66b369..cbe7c035afc8b 100644 --- a/src/plugins/inspector/public/views/requests/components/request_details.tsx +++ b/src/plugins/inspector/public/views/requests/components/request_details.tsx @@ -96,7 +96,7 @@ export function RequestDetails(props: Props) { ))} - + ) : null; } diff --git a/src/plugins/kibana_usage_collection/server/collectors/ops_stats/__snapshots__/ops_stats_collector.test.ts.snap b/src/plugins/kibana_usage_collection/server/collectors/ops_stats/__snapshots__/ops_stats_collector.test.ts.snap index 5f4aabf57995e..8464a88dfce5d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/ops_stats/__snapshots__/ops_stats_collector.test.ts.snap +++ b/src/plugins/kibana_usage_collection/server/collectors/ops_stats/__snapshots__/ops_stats_collector.test.ts.snap @@ -32,6 +32,8 @@ Object { "utilization": 1, }, "memory": Object { + "array_buffers_in_bytes": 1, + "external_in_bytes": 1, "heap": Object { "size_limit": 1, "total_in_bytes": 1, @@ -51,6 +53,8 @@ Object { "utilization": 1, }, "memory": Object { + "array_buffers_in_bytes": 1, + "external_in_bytes": 1, "heap": Object { "size_limit": 1, "total_in_bytes": 1, diff --git a/src/plugins/links/kibana.jsonc b/src/plugins/links/kibana.jsonc index 5f0796d55b43a..a058db8a03ce2 100644 --- a/src/plugins/links/kibana.jsonc +++ b/src/plugins/links/kibana.jsonc @@ -12,11 +12,12 @@ "dashboard", "embeddable", "kibanaReact", + "kibanaUtils", "presentationUtil", "uiActionsEnhanced", - "kibanaUtils" + "visualizations" ], - "optionalPlugins": ["triggersActionsUi"], + "optionalPlugins": ["triggersActionsUi", "usageCollection"], "requiredBundles": ["savedObjects"] } } diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx index 4ad4608080b52..30977b593238a 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.tsx @@ -12,6 +12,7 @@ import useAsync from 'react-use/lib/useAsync'; import useObservable from 'react-use/lib/useObservable'; import { EuiButtonEmpty, EuiListGroupItem } from '@elastic/eui'; +import { METRIC_TYPE } from '@kbn/analytics'; import { DashboardLocatorParams, getDashboardLocatorParamsFromEmbeddable, @@ -22,7 +23,13 @@ import { DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS, } from '@kbn/presentation-util-plugin/public'; -import { Link, LinksLayoutType, LINKS_VERTICAL_LAYOUT } from '../../../common/content_management'; +import { + DASHBOARD_LINK_TYPE, + Link, + LinksLayoutType, + LINKS_VERTICAL_LAYOUT, +} from '../../../common/content_management'; +import { trackUiMetric } from '../../services/kibana_services'; import { useLinks } from '../links_hooks'; import { DashboardLinkStrings } from './dashboard_link_strings'; import { fetchDashboard } from './dashboard_link_tools'; @@ -97,9 +104,9 @@ export const DashboardLinkComponent = ({ /** * Dashboard-to-dashboard navigation */ - const { loading: loadingOnClickProps, value: onClickProps } = useAsync(async () => { + const onClickProps = useMemo(() => { /** If the link points to the current dashboard, then there should be no `onClick` or `href` prop */ - if (link.destination === parentDashboardId) return; + if (!link.destination || link.destination === parentDashboardId) return; const linkOptions = { ...DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS, @@ -118,6 +125,8 @@ export const DashboardLinkComponent = ({ return { href, onClick: async (event: React.MouseEvent) => { + trackUiMetric?.(METRIC_TYPE.CLICK, `${DASHBOARD_LINK_TYPE}:click`); + /** * If the link is being opened via a modified click, then we should use the default `href` navigation behaviour * by passing all the dashboard state via the URL - this will keep behaviour consistent across all browsers. @@ -132,26 +141,19 @@ export const DashboardLinkComponent = ({ if (linkOptions.openInNewTab) { window.open(href, '_blank'); } else { - locator.navigate(params); + await locator.navigate(params); } }, }; - }, [link]); + }, [link, dashboardContainer.locator, linksEmbeddable, parentDashboardId]); useEffect(() => { - if (loadingDestinationDashboard || loadingOnClickProps) { + if (loadingDestinationDashboard) { onLoading(); } else { onRender(); } - }, [ - link, - linksEmbeddable, - loadingDestinationDashboard, - loadingOnClickProps, - onLoading, - onRender, - ]); + }, [link, linksEmbeddable, loadingDestinationDashboard, onLoading, onRender]); const id = `dashboardLink--${link.id}`; @@ -178,7 +180,7 @@ export const DashboardLinkComponent = ({ }} iconType={error ? 'warning' : undefined} iconProps={{ className: 'dashboardLinkIcon' }} - isDisabled={Boolean(error) || loadingOnClickProps} + isDisabled={Boolean(error)} className={classNames('linksPanelLink', { linkCurrent: link.destination === parentDashboardId, dashboardLinkError: Boolean(error), diff --git a/src/plugins/links/public/components/external_link/external_link_component.tsx b/src/plugins/links/public/components/external_link/external_link_component.tsx index ac409cfbac4cf..4af95c83cc325 100644 --- a/src/plugins/links/public/components/external_link/external_link_component.tsx +++ b/src/plugins/links/public/components/external_link/external_link_component.tsx @@ -9,15 +9,21 @@ import React, { useMemo, useState } from 'react'; import useMount from 'react-use/lib/useMount'; +import { EuiListGroupItem } from '@elastic/eui'; +import { METRIC_TYPE } from '@kbn/analytics'; import { - UrlDrilldownOptions, DEFAULT_URL_DRILLDOWN_OPTIONS, + UrlDrilldownOptions, } from '@kbn/ui-actions-enhanced-plugin/public'; -import { EuiListGroupItem } from '@elastic/eui'; +import { + EXTERNAL_LINK_TYPE, + Link, + LinksLayoutType, + LINKS_VERTICAL_LAYOUT, +} from '../../../common/content_management'; +import { coreServices, trackUiMetric } from '../../services/kibana_services'; import { validateUrl } from './external_link_tools'; -import { coreServices } from '../../services/kibana_services'; -import { Link, LinksLayoutType, LINKS_VERTICAL_LAYOUT } from '../../../common/content_management'; export const ExternalLinkComponent = ({ link, @@ -78,6 +84,8 @@ export const ExternalLinkComponent = ({ onClick={async (event) => { if (!destination) return; + trackUiMetric?.(METRIC_TYPE.CLICK, `${EXTERNAL_LINK_TYPE}:click`); + /** Only use `navigateToUrl` if we **aren't** opening in a new window/tab; otherwise, just use default href handling */ const modifiedClick = event.ctrlKey || event.metaKey || event.shiftKey; if (!modifiedClick) { diff --git a/src/plugins/links/public/content_management/links_content_management_client.ts b/src/plugins/links/public/content_management/links_content_management_client.ts index 777fd8731d691..586b5aff5efb8 100644 --- a/src/plugins/links/public/content_management/links_content_management_client.ts +++ b/src/plugins/links/public/content_management/links_content_management_client.ts @@ -7,8 +7,9 @@ */ import type { SearchQuery } from '@kbn/content-management-plugin/common'; +import { SerializableAttributes, VisualizationClient } from '@kbn/visualizations-plugin/public'; +import { CONTENT_ID as contentTypeId, CONTENT_ID } from '../../common'; import type { LinksCrudTypes } from '../../common/content_management'; -import { CONTENT_ID as contentTypeId } from '../../common'; import { contentManagement } from '../services/kibana_services'; const get = async (id: string) => { @@ -65,3 +66,9 @@ export const linksClient = { delete: deleteLinks, search, }; + +export function getLinksClient< + Attr extends SerializableAttributes = SerializableAttributes +>(): VisualizationClient { + return linksClient as unknown as VisualizationClient; +} diff --git a/src/plugins/links/public/editor/open_editor_flyout.tsx b/src/plugins/links/public/editor/open_editor_flyout.tsx index 1c722a484eb1d..d47b178d5ff97 100644 --- a/src/plugins/links/public/editor/open_editor_flyout.tsx +++ b/src/plugins/links/public/editor/open_editor_flyout.tsx @@ -7,18 +7,20 @@ */ import React from 'react'; +import { skip, take } from 'rxjs/operators'; -import { withSuspense } from '@kbn/shared-ux-utility'; -import { toMountPoint } from '@kbn/react-kibana-mount'; import { EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; -import { tracksOverlays } from '@kbn/embeddable-plugin/public'; import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; +import { tracksOverlays } from '@kbn/embeddable-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { withSuspense } from '@kbn/shared-ux-utility'; -import { LinksInput, LinksByReferenceInput, LinksEditorFlyoutReturn } from '../embeddable/types'; -import { coreServices } from '../services/kibana_services'; -import { runSaveToLibrary } from '../content_management/save_to_library'; +import { OverlayRef } from '@kbn/core-mount-utils-browser'; import { Link, LinksLayoutType } from '../../common/content_management'; +import { runSaveToLibrary } from '../content_management/save_to_library'; +import { LinksByReferenceInput, LinksEditorFlyoutReturn, LinksInput } from '../embeddable/types'; import { getLinksAttributeService } from '../services/attribute_service'; +import { coreServices } from '../services/kibana_services'; const LazyLinksEditor = React.lazy(() => import('../components/editor/links_editor')); @@ -40,7 +42,8 @@ export async function openEditorFlyout( const { attributes } = await attributeService.unwrapAttributes(initialInput); const isByReference = attributeService.inputIsRefType(initialInput); const initialLinks = attributes?.links; - const overlayTracker = tracksOverlays(parentDashboard) ? parentDashboard : undefined; + const overlayTracker = + parentDashboard && tracksOverlays(parentDashboard) ? parentDashboard : undefined; if (!initialLinks) { /** @@ -55,6 +58,22 @@ export async function openEditorFlyout( } return new Promise((resolve, reject) => { + const closeEditorFlyout = (editorFlyout: OverlayRef) => { + if (overlayTracker) { + overlayTracker.clearOverlays(); + } else { + editorFlyout.close(); + } + }; + + /** + * Close the flyout whenever the app changes - this handles cases for when the flyout is open outside of the + * Dashboard app (`overlayTracker` is not available) + */ + coreServices.application.currentAppId$.pipe(skip(1), take(1)).subscribe(() => { + if (!overlayTracker) editorFlyout.close(); + }); + const onSaveToLibrary = async (newLinks: Link[], newLayout: LinksLayoutType) => { const newAttributes = { ...attributes, @@ -74,7 +93,7 @@ export async function openEditorFlyout( attributes: newAttributes, }); parentDashboard?.reload(); - if (overlayTracker) overlayTracker.clearOverlays(); + closeEditorFlyout(editorFlyout); }; const onAddToDashboard = (newLinks: Link[], newLayout: LinksLayoutType) => { @@ -94,12 +113,12 @@ export async function openEditorFlyout( attributes: newAttributes, }); parentDashboard?.reload(); - if (overlayTracker) overlayTracker.clearOverlays(); + closeEditorFlyout(editorFlyout); }; const onCancel = () => { reject(); - if (overlayTracker) overlayTracker.clearOverlays(); + closeEditorFlyout(editorFlyout); }; const editorFlyout = coreServices.overlays.openFlyout( @@ -125,6 +144,8 @@ export async function openEditorFlyout( } ); - if (overlayTracker) overlayTracker.openOverlay(editorFlyout); + if (overlayTracker) { + overlayTracker.openOverlay(editorFlyout); + } }); } diff --git a/src/plugins/links/public/embeddable/links_embeddable_factory.ts b/src/plugins/links/public/embeddable/links_embeddable_factory.ts index 55838c6d65229..e1446aff316af 100644 --- a/src/plugins/links/public/embeddable/links_embeddable_factory.ts +++ b/src/plugins/links/public/embeddable/links_embeddable_factory.ts @@ -125,8 +125,6 @@ export class LinksFactoryDefinition initialInput: LinksInput, parent?: DashboardContainer ): Promise { - if (!parent) return { newInput: {} }; - const { openEditorFlyout } = await import('../editor/open_editor_flyout'); const { newInput, attributes } = await openEditorFlyout( diff --git a/src/plugins/links/public/plugin.ts b/src/plugins/links/public/plugin.ts index 7927de88b80e7..f72f45d4c6a22 100644 --- a/src/plugins/links/public/plugin.ts +++ b/src/plugins/links/public/plugin.ts @@ -6,22 +6,29 @@ * Side Public License, v 1. */ -import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { ContentManagementPublicSetup, ContentManagementPublicStart, } from '@kbn/content-management-plugin/public'; +import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { DashboardStart } from '@kbn/dashboard-plugin/public'; +import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container'; import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; -import { APP_NAME } from '../common'; +import { APP_ICON, APP_NAME, CONTENT_ID, LATEST_VERSION } from '../common'; +import { LinksCrudTypes } from '../common/content_management'; +import { LinksStrings } from './components/links_strings'; +import { getLinksClient } from './content_management/links_content_management_client'; import { LinksFactoryDefinition } from './embeddable'; -import { CONTENT_ID, LATEST_VERSION } from '../common'; +import { LinksByReferenceInput } from './embeddable/types'; import { setKibanaServices } from './services/kibana_services'; export interface LinksSetupDependencies { embeddable: EmbeddableSetup; + visualizations: VisualizationsSetup; contentManagement: ContentManagementPublicSetup; } @@ -30,6 +37,7 @@ export interface LinksStartDependencies { dashboard: DashboardStart; presentationUtil: PresentationUtilPluginStart; contentManagement: ContentManagementPublicStart; + usageCollection?: UsageCollectionStart; } export class LinksPlugin @@ -39,7 +47,9 @@ export class LinksPlugin public setup(core: CoreSetup, plugins: LinksSetupDependencies) { core.getStartServices().then(([_, deps]) => { - plugins.embeddable.registerEmbeddableFactory(CONTENT_ID, new LinksFactoryDefinition()); + const linksFactory = new LinksFactoryDefinition(); + + plugins.embeddable.registerEmbeddableFactory(CONTENT_ID, linksFactory); plugins.contentManagement.registry.register({ id: CONTENT_ID, @@ -48,6 +58,53 @@ export class LinksPlugin }, name: APP_NAME, }); + + const getExplicitInput = async ({ + savedObjectId, + parent, + }: { + savedObjectId?: string; + parent?: DashboardContainer; + }) => { + try { + await linksFactory.getExplicitInput({ savedObjectId } as LinksByReferenceInput, parent); + } catch { + // swallow any errors - this just means that the user cancelled editing + } + return; + }; + + plugins.visualizations.registerAlias({ + disableCreate: true, // do not allow creation through visualization listing page + name: CONTENT_ID, + title: APP_NAME, + icon: APP_ICON, + description: LinksStrings.getDescription(), + stage: 'experimental', + appExtensions: { + visualizations: { + docTypes: [CONTENT_ID], + searchFields: ['title^3'], + client: getLinksClient, + toListItem(linkItem: LinksCrudTypes['Item']) { + const { id, type, updatedAt, attributes } = linkItem; + const { title, description } = attributes; + + return { + id, + title, + editor: { onEdit: (savedObjectId: string) => getExplicitInput({ savedObjectId }) }, + description, + updatedAt, + icon: APP_ICON, + typeTitle: APP_NAME, + stage: 'experimental', + savedObjectType: type, + }; + }, + }, + }, + }); }); } diff --git a/src/plugins/links/public/services/kibana_services.ts b/src/plugins/links/public/services/kibana_services.ts index 76acd242f7575..7536c12262792 100644 --- a/src/plugins/links/public/services/kibana_services.ts +++ b/src/plugins/links/public/services/kibana_services.ts @@ -8,12 +8,13 @@ import { BehaviorSubject } from 'rxjs'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { CoreStart } from '@kbn/core/public'; import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; -import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import { CONTENT_ID } from '../../common'; import { LinksStartDependencies } from '../plugin'; export let coreServices: CoreStart; @@ -21,6 +22,11 @@ export let dashboardServices: DashboardStart; export let embeddableService: EmbeddableStart; export let presentationUtil: PresentationUtilPluginStart; export let contentManagement: ContentManagementPublicStart; +export let trackUiMetric: ( + type: string, + eventNames: string | string[], + count?: number +) => void | undefined; const servicesReady$ = new BehaviorSubject(false); @@ -42,6 +48,8 @@ export const setKibanaServices = (kibanaCore: CoreStart, deps: LinksStartDepende embeddableService = deps.embeddable; presentationUtil = deps.presentationUtil; contentManagement = deps.contentManagement; + if (deps.usageCollection) + trackUiMetric = deps.usageCollection.reportUiCounter.bind(deps.usageCollection, CONTENT_ID); servicesReady$.next(true); }; diff --git a/src/plugins/links/tsconfig.json b/src/plugins/links/tsconfig.json index e9814f4e107e7..f839243325d07 100644 --- a/src/plugins/links/tsconfig.json +++ b/src/plugins/links/tsconfig.json @@ -26,7 +26,11 @@ "@kbn/logging", "@kbn/core-plugins-server", "@kbn/react-kibana-mount", - "@kbn/react-kibana-context-theme" + "@kbn/react-kibana-context-theme", + "@kbn/analytics", + "@kbn/usage-collection-plugin", + "@kbn/visualizations-plugin", + "@kbn/core-mount-utils-browser" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx index 1804a2fcf2046..8e1c7fbc74b99 100644 --- a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx +++ b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx @@ -9,6 +9,7 @@ import React, { useState } from 'react'; import { EuiSelectable, EuiInputPopover, EuiSelectableProps } from '@elastic/eui'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import { ToolbarButton, ToolbarButtonProps } from '@kbn/shared-ux-button-toolbar'; @@ -67,6 +68,7 @@ export function DataViewPicker({ isOpen={isPopoverOpen} input={createTrigger()} closePopover={() => setPopoverIsOpen(false)} + panelMinWidth={calculateWidthFromEntries(dataViews, ['name', 'id'])} panelProps={{ 'data-test-subj': 'data-view-picker-popover', }} diff --git a/src/plugins/presentation_util/tsconfig.json b/src/plugins/presentation_util/tsconfig.json index e17fd6cc5a754..4076319587e17 100644 --- a/src/plugins/presentation_util/tsconfig.json +++ b/src/plugins/presentation_util/tsconfig.json @@ -31,7 +31,8 @@ "@kbn/ui-actions-plugin", "@kbn/saved-objects-finder-plugin", "@kbn/content-management-plugin", - "@kbn/shared-ux-button-toolbar" + "@kbn/shared-ux-button-toolbar", + "@kbn/calculate-width-from-char-count" ], "exclude": ["target/**/*"] } diff --git a/src/plugins/telemetry/server/fetcher.test.ts b/src/plugins/telemetry/server/fetcher.test.ts index e1da374dc7239..5a61de1a0d83e 100644 --- a/src/plugins/telemetry/server/fetcher.test.ts +++ b/src/plugins/telemetry/server/fetcher.test.ts @@ -195,6 +195,61 @@ describe('FetcherTask', () => { }) ); + test( + 'Retries when `getCurrentConfigs` rejects', + fakeSchedulers(async (advance) => { + expect(fetcherTask['isOnline$'].value).toBe(false); + getCurrentConfigs.mockRejectedValueOnce(new Error('SomeError')).mockResolvedValue({ + telemetryOptIn: true, + telemetrySendUsageFrom: 'server', + failureCount: 0, + telemetryUrl: 'test-url', + }); + fetchMock.mockResolvedValue({}); + + const subscription = fetcherTask['validateConnectivity'](); + + // need to advance / await twice for retry + advance(5 * 60 * 1000); + await new Promise((resolve) => process.nextTick(resolve)); // Wait for the initial promise to fulfill + advance(1 * 60 * 1000); + await new Promise((resolve) => process.nextTick(resolve)); // Wait for the retry promise to fulfill + + expect(getCurrentConfigs).toHaveBeenCalledTimes(2); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(updateReportFailure).toHaveBeenCalledTimes(0); + expect(fetcherTask['isOnline$'].value).toBe(true); + + subscription.unsubscribe(); + }) + ); + + test( + 'logs a message when retries are exceeded', + fakeSchedulers(async (advance) => { + expect(fetcherTask['isOnline$'].value).toBe(false); + getCurrentConfigs.mockRejectedValue(new Error('SomeError')); + + const subscription = fetcherTask['validateConnectivity'](); + + const wait = async () => { + advance(5 * 60 * 1000); + await new Promise((resolve) => process.nextTick(resolve)); // Wait for the initial promise to fulfill + }; + + for (let i = 0; i < 7; i++) { + await wait(); + } + + expect(fetcherTask['logger'].error).toBeCalledTimes(1); + expect(fetcherTask['logger'].error).toHaveBeenCalledWith( + `Cannot get the current config: SomeError` + ); + + subscription.unsubscribe(); + }) + ); + test( 'Should not retry if it hit the max number of failures for this version', fakeSchedulers(async (advance) => { diff --git a/src/plugins/telemetry/server/fetcher.ts b/src/plugins/telemetry/server/fetcher.ts index a79e043551775..756c9f49ded62 100644 --- a/src/plugins/telemetry/server/fetcher.ts +++ b/src/plugins/telemetry/server/fetcher.ts @@ -18,6 +18,10 @@ import { Subscription, takeUntil, timer, + catchError, + defer, + EMPTY, + retry, } from 'rxjs'; import fetch from 'node-fetch'; import type { TelemetryCollectionManagerPluginStart } from '@kbn/telemetry-collection-manager-plugin/server'; @@ -105,7 +109,25 @@ export class FetcherTask { // Skip any further processing if already online filter(() => !this.isOnline$.value), // Fetch current state and configs - exhaustMap(async () => await this.getCurrentConfigs()), + exhaustMap(() => { + return defer(() => { + return this.getCurrentConfigs(); + }).pipe( + // exp-backoff retries in case of errors fetching the config + retry({ + count: 5, + delay: (error, retryIndex) => { + const retryDelay = 1000 * Math.min(Math.pow(2, retryIndex + 2), 64); // 5 retries -> 8s, 16s, 32s, 64s, 64s + return timer(retryDelay); + }, + }), + // shallow errors if all retry failed, next time tick will continue the emission + catchError((err) => { + this.logger.error(`Cannot get the current config: ${err.message}`); + return EMPTY; + }) + ); + }), // Skip if opted-out, or should only send from the browser filter( ({ telemetryOptIn, telemetrySendUsageFrom }) => diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index ede7098b7e916..d996b6b4e2cdc 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -31,14 +31,11 @@ export { visualizeGeoFieldTrigger, ROW_CLICK_TRIGGER, rowClickTrigger, - CATEGORIZE_FIELD_TRIGGER, - categorizeFieldTrigger, } from '@kbn/ui-actions-browser/src/triggers'; -export type { VisualizeFieldContext, CategorizeFieldContext } from './types'; +export type { VisualizeFieldContext } from './types'; export { ACTION_VISUALIZE_FIELD, ACTION_VISUALIZE_GEO_FIELD, ACTION_VISUALIZE_LENS_FIELD, - ACTION_CATEGORIZE_FIELD, } from './types'; export type { ActionExecutionContext, ActionExecutionMeta, ActionMenuItemProps } from './actions'; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 52629aa3f572d..16060d4bf3435 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -9,7 +9,6 @@ import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/public'; import { PublicMethodsOf } from '@kbn/utility-types'; import { - categorizeFieldTrigger, rowClickTrigger, visualizeFieldTrigger, visualizeGeoFieldTrigger, @@ -39,7 +38,6 @@ export class UiActionsPlugin implements Plugin { this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); this.service.registerTrigger(visualizeGeoFieldTrigger); - this.service.registerTrigger(categorizeFieldTrigger); return this.service; } diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 6d689f6776d96..c66ea2b1769bd 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -7,7 +7,7 @@ */ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { AggregateQuery } from '@kbn/es-query'; -import type { DataViewField, DataViewSpec, DataView } from '@kbn/data-views-plugin/public'; +import type { DataViewSpec } from '@kbn/data-views-plugin/public'; import { ActionInternal } from './actions/action_internal'; import { TriggerInternal } from './triggers/trigger_internal'; @@ -24,13 +24,6 @@ export interface VisualizeFieldContext { query?: AggregateQuery; } -export interface CategorizeFieldContext { - field: DataViewField; - dataView: DataView; - originatingApp: string; -} - export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; export const ACTION_VISUALIZE_LENS_FIELD = 'ACTION_VISUALIZE_LENS_FIELD'; -export const ACTION_CATEGORIZE_FIELD = 'ACTION_CATEGORIZE_FIELD'; diff --git a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx index e3e4059ad3cf5..77e00e157d62b 100644 --- a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx +++ b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx @@ -9,6 +9,7 @@ import { EuiComboBox, EuiComboBoxOptionOption, EuiToolTip, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; import { UnifiedHistogramBreakdownContext } from '../types'; @@ -59,11 +60,10 @@ export const BreakdownFieldSelector = ({ const breakdownCss = css` width: 100%; max-width: ${euiTheme.base * 22}px; - &:focus-within { - max-width: ${euiTheme.base * 30}px; - } `; + const panelMinWidth = calculateWidthFromEntries(fieldOptions, ['label']); + return ( ; isOnHistogramMode?: boolean; + histogramQuery?: AggregateQuery; isChartLoading?: boolean; onResetChartHeight?: () => void; onChartHiddenChange?: (chartHidden: boolean) => void; @@ -115,6 +116,7 @@ export function Chart({ lensAdapters, lensEmbeddableOutput$, isOnHistogramMode, + histogramQuery, isChartLoading, onResetChartHeight, onChartHiddenChange, @@ -216,7 +218,7 @@ export function Chart({ getLensAttributes({ title: chart?.title, filters, - query, + query: histogramQuery ?? query, dataView, timeInterval: chart?.timeInterval, breakdownField: breakdown?.field, @@ -230,6 +232,7 @@ export function Chart({ dataView, filters, query, + histogramQuery, ] ); diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 486ea7da79872..5ed329f251a97 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -68,6 +68,7 @@ export function ChartConfigPanel({ updatePanelState={updateSuggestion} lensAdapters={lensAdapters} output$={lensEmbeddableOutput$} + displayFlyoutHeader closeFlyout={() => { setIsFlyoutVisible(false); }} diff --git a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.test.ts b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.test.ts index d891c62df3514..119356af6f63f 100644 --- a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.test.ts +++ b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.test.ts @@ -38,6 +38,7 @@ describe('useLensSuggestions', () => { allSuggestions: [], currentSuggestion: undefined, isOnHistogramMode: false, + histogramQuery: undefined, suggestionUnsupported: false, }); }); @@ -66,6 +67,7 @@ describe('useLensSuggestions', () => { allSuggestions: allSuggestionsMock, currentSuggestion: allSuggestionsMock[0], isOnHistogramMode: false, + histogramQuery: undefined, suggestionUnsupported: false, }); }); @@ -94,6 +96,7 @@ describe('useLensSuggestions', () => { allSuggestions: [], currentSuggestion: undefined, isOnHistogramMode: false, + histogramQuery: undefined, suggestionUnsupported: true, }); }); @@ -133,10 +136,52 @@ describe('useLensSuggestions', () => { allSuggestions: [], currentSuggestion: allSuggestionsMock[0], isOnHistogramMode: true, + histogramQuery: { + esql: 'from the-data-view | limit 100 | EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats rows = count(*) by timestamp | rename timestamp as `@timestamp every 30 minute`', + }, suggestionUnsupported: false, }); }); + test('should return histogramSuggestion even if the ESQL query contains a DROP @timestamp statement', async () => { + const firstMockReturn = undefined; + const secondMockReturn = allSuggestionsMock; + const lensSuggestionsApi = jest + .fn() + .mockReturnValueOnce(firstMockReturn) // will return to firstMockReturn object firstly + .mockReturnValueOnce(secondMockReturn); // will return to secondMockReturn object secondly + + renderHook(() => { + return useLensSuggestions({ + dataView: dataViewMock, + query: { esql: 'from the-data-view | DROP @timestamp | limit 100' }, + isPlainRecord: true, + columns: [ + { + id: 'var0', + name: 'var0', + meta: { + type: 'number', + }, + }, + ], + data: dataMock, + lensSuggestionsApi, + timeRange: { + from: '2023-09-03T08:00:00.000Z', + to: '2023-09-04T08:56:28.274Z', + }, + }); + }); + expect(lensSuggestionsApi).toHaveBeenLastCalledWith( + expect.objectContaining({ + query: { esql: expect.stringMatching('from the-data-view | limit 100 ') }, + }), + expect.anything(), + ['lnsDatatable'] + ); + }); + test('should not return histogramSuggestion if no suggestions returned by the api and transformational commands', async () => { const firstMockReturn = undefined; const secondMockReturn = allSuggestionsMock; @@ -172,6 +217,7 @@ describe('useLensSuggestions', () => { allSuggestions: [], currentSuggestion: undefined, isOnHistogramMode: false, + histogramQuery: undefined, suggestionUnsupported: true, }); }); diff --git a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts index 514c32cefcb80..063e1b7ef89a2 100644 --- a/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts +++ b/src/plugins/unified_histogram/public/layout/hooks/use_lens_suggestions.ts @@ -11,6 +11,7 @@ import { AggregateQuery, isOfAggregateQueryType, getAggregateQueryMode, + cleanupESQLQueryForLensSuggestions, Query, TimeRange, } from '@kbn/es-query'; @@ -61,7 +62,7 @@ export const useLensSuggestions = ({ const [allSuggestions, setAllSuggestions] = useState(suggestions.allSuggestions); const currentSuggestion = originalSuggestion ?? suggestions.firstSuggestion; const suggestionDeps = useRef(getSuggestionDeps({ dataView, query, columns })); - + const histogramQuery = useRef(); const histogramSuggestion = useMemo(() => { if ( !currentSuggestion && @@ -85,8 +86,8 @@ export const useLensSuggestions = ({ const interval = computeInterval(timeRange, data); const language = getAggregateQueryMode(query); - const histogramQuery = `${query[language]} - | EVAL timestamp=DATE_TRUNC(${interval}, ${dataView.timeFieldName}) | stats rows = count(*) by timestamp | rename timestamp as \`${dataView.timeFieldName} every ${interval}\``; + const safeQuery = cleanupESQLQueryForLensSuggestions(query[language]); + const esqlQuery = `${safeQuery} | EVAL timestamp=DATE_TRUNC(${interval}, ${dataView.timeFieldName}) | stats rows = count(*) by timestamp | rename timestamp as \`${dataView.timeFieldName} every ${interval}\``; const context = { dataViewSpec: dataView?.toSpec(), fieldName: '', @@ -107,15 +108,16 @@ export const useLensSuggestions = ({ }, ] as DatatableColumn[], query: { - esql: histogramQuery, + esql: esqlQuery, }, }; const sug = lensSuggestionsApi(context, dataView, ['lnsDatatable']) ?? []; if (sug.length) { + histogramQuery.current = { esql: esqlQuery }; return sug[0]; } - return undefined; } + histogramQuery.current = undefined; return undefined; }, [currentSuggestion, dataView, query, timeRange, data, lensSuggestionsApi]); @@ -142,6 +144,7 @@ export const useLensSuggestions = ({ currentSuggestion: histogramSuggestion ?? currentSuggestion, suggestionUnsupported: !currentSuggestion && !histogramSuggestion && isPlainRecord, isOnHistogramMode: Boolean(histogramSuggestion), + histogramQuery: histogramQuery.current ? histogramQuery.current : undefined, }; }; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index d923ea3031a50..17eaf65fcde5f 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -215,18 +215,23 @@ export const UnifiedHistogramLayout = ({ children, withDefaultActions, }: UnifiedHistogramLayoutProps) => { - const { allSuggestions, currentSuggestion, suggestionUnsupported, isOnHistogramMode } = - useLensSuggestions({ - dataView, - query, - originalSuggestion, - isPlainRecord, - columns, - timeRange, - data: services.data, - lensSuggestionsApi, - onSuggestionChange, - }); + const { + allSuggestions, + currentSuggestion, + suggestionUnsupported, + isOnHistogramMode, + histogramQuery, + } = useLensSuggestions({ + dataView, + query, + originalSuggestion, + isPlainRecord, + columns, + timeRange, + data: services.data, + lensSuggestionsApi, + onSuggestionChange, + }); const chart = suggestionUnsupported ? undefined : originalChart; const [topPanelNode] = useState(() => @@ -302,6 +307,7 @@ export const UnifiedHistogramLayout = ({ lensAdapters={lensAdapters} lensEmbeddableOutput$={lensEmbeddableOutput$} isOnHistogramMode={isOnHistogramMode} + histogramQuery={histogramQuery} withDefaultActions={withDefaultActions} /> diff --git a/src/plugins/unified_histogram/tsconfig.json b/src/plugins/unified_histogram/tsconfig.json index b8337379679c3..e381b7ee3cf02 100644 --- a/src/plugins/unified_histogram/tsconfig.json +++ b/src/plugins/unified_histogram/tsconfig.json @@ -26,6 +26,7 @@ "@kbn/visualizations-plugin", "@kbn/discover-utils", "@kbn/resizable-layout", + "@kbn/calculate-width-from-char-count", ], "exclude": [ "target/**/*", diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts b/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts index 1c505752d392c..a2332648da3ea 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.styles.ts @@ -6,15 +6,24 @@ * Side Public License, v 1. */ -export const DATA_VIEW_POPOVER_CONTENT_WIDTH = 280; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; +import { DataViewListItemEnhanced } from './dataview_list'; -export const changeDataViewStyles = ({ fullWidth }: { fullWidth?: boolean }) => { +const MIN_WIDTH = 300; + +export const changeDataViewStyles = ({ + fullWidth, + dataViewsList, +}: { + fullWidth?: boolean; + dataViewsList: DataViewListItemEnhanced[]; +}) => { return { trigger: { - maxWidth: fullWidth ? undefined : DATA_VIEW_POPOVER_CONTENT_WIDTH, + maxWidth: fullWidth ? undefined : MIN_WIDTH, }, popoverContent: { - width: DATA_VIEW_POPOVER_CONTENT_WIDTH, + width: calculateWidthFromEntries(dataViewsList, ['name', 'id'], { minWidth: MIN_WIDTH }), }, }; }; diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index e1565f1ff6b0d..9076bcb37c7df 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -96,7 +96,9 @@ export function ChangeDataView({ const { application, data, storage, dataViews, dataViewEditor, appName, usageCollection } = kibana.services; const reportUiCounter = usageCollection?.reportUiCounter.bind(usageCollection, appName); - const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); + + const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth, dataViewsList }); + const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() => Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY)) ); @@ -215,7 +217,7 @@ export function ChangeDataView({ })} ) : ( - + ), ); @@ -336,13 +338,23 @@ export function ChangeDataView({ if (textBasedLanguages?.length) { panelItems.push( , - + onTextBasedSubmit({ esql: `from ${trigger.title} | limit 10` })} data-test-subj="select-text-based-language-panel" + contentProps={{ + css: { + justifyContent: 'flex-start', + paddingLeft: '26px', + }, + }} > {i18n.translate('unifiedSearch.query.queryBar.textBasedLanguagesTryLabel', { defaultMessage: 'Try ES|QL', diff --git a/src/plugins/unified_search/public/filter_bar/filter_bar.tsx b/src/plugins/unified_search/public/filter_bar/filter_bar.tsx index 769d6bfcaf48f..a9c53d9f89121 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_bar.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_bar.tsx @@ -55,6 +55,8 @@ const FilterBarUI = React.memo(function FilterBarUI(props: Props) { gutterSize="none" // We use `gap` in the styles instead for better truncation of badges alignItems="center" tabIndex={-1} + data-test-subj="filter-items-group" + className={`filter-items-group ${props.className ?? ''}`} > {props.prepend} { - comboBoxWrapperRef = React.createRef(); inputRef: HTMLInputElement | null = null; public render() { @@ -59,43 +59,39 @@ class PhraseValueInputUI extends PhraseSuggestorUI { // there are cases when the value is a number, this would cause an exception const valueAsStr = String(value); const options = value ? uniq([valueAsStr, ...suggestions]) : suggestions; + const panelMinWidth = calculateWidthFromEntries(options); return ( -
    - { - this.inputRef = ref; - }} - isDisabled={this.props.disabled} - fullWidth={fullWidth} - compressed={this.props.compressed} - placeholder={intl.formatMessage({ - id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder', - defaultMessage: 'Select a value', - })} - aria-label={intl.formatMessage({ - id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder', - defaultMessage: 'Select a value', - })} - options={options} - getLabel={(option) => option} - selectedOptions={value ? [valueAsStr] : []} - onChange={([newValue = '']) => { - onChange(newValue); - setTimeout(() => { - // Note: requires a tick skip to correctly blur element focus - this.inputRef?.blur(); - }); - }} - onSearchChange={this.onSearchChange} - onCreateOption={onChange} - isClearable={false} - data-test-subj="filterParamsComboBox phraseParamsComboxBox" - singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS} - truncationProps={MIDDLE_TRUNCATION_PROPS} - /> -
    + { + this.inputRef = ref; + }} + isDisabled={this.props.disabled} + fullWidth={fullWidth} + compressed={this.props.compressed} + placeholder={intl.formatMessage({ + id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder', + defaultMessage: 'Select a value', + })} + aria-label={intl.formatMessage({ + id: 'unifiedSearch.filter.filterEditor.valueSelectPlaceholder', + defaultMessage: 'Select a value', + })} + options={options} + getLabel={(option) => option} + selectedOptions={value ? [valueAsStr] : []} + onChange={([newValue = '']) => { + onChange(newValue); + }} + onSearchChange={this.onSearchChange} + onCreateOption={onChange} + isClearable={false} + data-test-subj="filterParamsComboBox phraseParamsComboxBox" + singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS} + truncationProps={MIDDLE_TRUNCATION_PROPS} + inputPopoverProps={{ panelMinWidth, anchorPosition: 'downRight' }} + /> ); } } diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx index 500b875f42667..30fd03fb3d9c2 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/phrases_values_input.tsx @@ -11,6 +11,7 @@ import { uniq } from 'lodash'; import React from 'react'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import { withEuiTheme, WithEuiThemeProps } from '@elastic/eui'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box'; import { PhraseSuggestorUI, PhraseSuggestorProps } from './phrase_suggestor'; import { phrasesValuesComboboxCss } from './phrases_values_input.styles'; @@ -28,45 +29,42 @@ interface Props { export type PhrasesValuesInputProps = Props & PhraseSuggestorProps & WithEuiThemeProps; class PhrasesValuesInputUI extends PhraseSuggestorUI { - comboBoxWrapperRef = React.createRef(); - public render() { const { suggestions, isLoading } = this.state; const { values, intl, onChange, fullWidth, onParamsUpdate, compressed, disabled } = this.props; const options = values ? uniq([...values, ...suggestions]) : suggestions; - + const panelMinWidth = calculateWidthFromEntries(options); return ( -
    - option} - selectedOptions={values || []} - onSearchChange={this.onSearchChange} - onCreateOption={(option: string) => { - onParamsUpdate(option.trim()); - }} - className={phrasesValuesComboboxCss(this.props.theme)} - onChange={onChange} - isClearable={false} - data-test-subj="filterParamsComboBox phrasesParamsComboxBox" - isDisabled={disabled} - truncationProps={MIDDLE_TRUNCATION_PROPS} - /> -
    + option} + selectedOptions={values || []} + onSearchChange={this.onSearchChange} + onCreateOption={(option: string) => { + onParamsUpdate(option.trim()); + }} + className={phrasesValuesComboboxCss(this.props.theme)} + onChange={onChange} + isClearable={false} + data-test-subj="filterParamsComboBox phrasesParamsComboxBox" + isDisabled={disabled} + truncationProps={MIDDLE_TRUNCATION_PROPS} + inputPopoverProps={{ panelMinWidth, anchorPosition: 'downRight' }} + /> ); } } diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx b/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx index 540226caef525..cc87c3de78936 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx +++ b/src/plugins/unified_search/public/filters_builder/filter_item/field_input.tsx @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import { FieldIcon } from '@kbn/react-field'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import { useGeneratedHtmlId, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import { getFilterableFields } from '../../filter_bar/filter_editor'; import { FiltersBuilderContextType } from '../context'; @@ -36,7 +37,6 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) const { disabled, suggestionsAbstraction } = useContext(FiltersBuilderContextType); const fields = dataView ? getFilterableFields(dataView) : []; const id = useGeneratedHtmlId({ prefix: 'fieldInput' }); - const comboBoxWrapperRef = useRef(null); const inputRef = useRef(null); const onFieldChange = useCallback( @@ -72,40 +72,30 @@ export function FieldInput({ field, dataView, onHandleField }: FieldInputProps) ({ label }) => fields[optionFields.findIndex((optionField) => optionField.label === label)] ); onFieldChange(newValues); - - setTimeout(() => { - // Note: requires a tick skip to correctly blur element focus - inputRef?.current?.blur(); - }); }; - const handleFocus: React.FocusEventHandler = () => { - // Force focus on input due to https://github.com/elastic/eui/issues/7170 - inputRef?.current?.focus(); - }; + const panelMinWidth = calculateWidthFromEntries(euiOptions, ['label']); return ( -
    - { - inputRef.current = ref; - }} - options={euiOptions} - selectedOptions={selectedEuiOptions} - onChange={onComboBoxChange} - isDisabled={disabled} - placeholder={strings.getFieldSelectPlaceholderLabel()} - sortMatchesBy="startsWith" - aria-label={strings.getFieldSelectPlaceholderLabel()} - isClearable={false} - compressed - fullWidth - onFocus={handleFocus} - data-test-subj="filterFieldSuggestionList" - singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS} - truncationProps={MIDDLE_TRUNCATION_PROPS} - /> -
    + { + inputRef.current = ref; + }} + options={euiOptions} + selectedOptions={selectedEuiOptions} + onChange={onComboBoxChange} + isDisabled={disabled} + placeholder={strings.getFieldSelectPlaceholderLabel()} + sortMatchesBy="startsWith" + aria-label={strings.getFieldSelectPlaceholderLabel()} + isClearable={false} + compressed + fullWidth + data-test-subj="filterFieldSuggestionList" + singleSelection={SINGLE_SELECTION_AS_TEXT_PROPS} + truncationProps={MIDDLE_TRUNCATION_PROPS} + inputPopoverProps={{ panelMinWidth }} + /> ); } diff --git a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts index 6ec0ac9ab7058..78c4952aa69b0 100644 --- a/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts +++ b/src/plugins/unified_search/public/filters_builder/filter_item/filter_item.styles.ts @@ -26,9 +26,6 @@ export const fieldAndParamCss = (euiTheme: EuiThemeComputed) => css` .euiFormRow { max-width: 800px; } - &:focus-within { - flex-grow: 4; - } `; export const operationCss = (euiTheme: EuiThemeComputed) => css` diff --git a/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx b/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx index f7148db93ce19..d8517eedba4ed 100644 --- a/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/unified_search/public/index_pattern_select/index_pattern_select.tsx @@ -11,7 +11,9 @@ import React, { Component } from 'react'; import { Required } from '@kbn/utility-types'; import { EuiComboBox, EuiComboBoxProps } from '@elastic/eui'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { MIDDLE_TRUNCATION_PROPS } from '../filter_bar/filter_editor/lib/helpers'; export type IndexPatternSelectProps = Required< Omit, 'onSearchChange' | 'options' | 'selectedOptions' | 'onChange'>, @@ -28,7 +30,7 @@ export type IndexPatternSelectInternalProps = IndexPatternSelectProps & { interface IndexPatternSelectState { isLoading: boolean; - options: []; + options: Array<{ value: string; label: string }>; selectedIndexPattern: { value: string; label: string } | undefined; searchValue: string | undefined; } @@ -147,6 +149,8 @@ export default class IndexPatternSelect extends Component ); } diff --git a/src/plugins/unified_search/tsconfig.json b/src/plugins/unified_search/tsconfig.json index f83de4ff80fc7..0412bbc4c8c98 100644 --- a/src/plugins/unified_search/tsconfig.json +++ b/src/plugins/unified_search/tsconfig.json @@ -42,6 +42,7 @@ "@kbn/core-doc-links-browser", "@kbn/core-lifecycle-browser", "@kbn/ml-string-hash", + "@kbn/calculate-width-from-char-count" ], "exclude": [ "target/**/*", diff --git a/src/plugins/usage_collection/common/types/stats/core_metrics.ts b/src/plugins/usage_collection/common/types/stats/core_metrics.ts index 64e5ef34155d9..5bc2023afc041 100644 --- a/src/plugins/usage_collection/common/types/stats/core_metrics.ts +++ b/src/plugins/usage_collection/common/types/stats/core_metrics.ts @@ -89,6 +89,10 @@ export interface OpsProcessMetrics { }; /** node rss */ resident_set_size_in_bytes: number; + /** memory usage of C++ objects bound to JavaScript objects managed by V8 */ + external_in_bytes: number; + /** memory allocated for array buffers. This is also included in the external value*/ + array_buffers_in_bytes: number; }; /** mean event loop delay since last collection*/ event_loop_delay: number; @@ -159,6 +163,14 @@ export interface OpsOsMetrics { time_throttled_nanos: number; }; }; + + /** memory cgroup metrics, undefined when not running in cgroup v2 */ + cgroup_memory?: { + /** The total amount of memory currently being used by the cgroup and its descendants. */ + current_in_bytes: number; + /** The total amount of swap currently being used by the cgroup and its descendants. */ + swap_current_in_bytes: number; + }; } /** diff --git a/src/plugins/usage_collection/server/collector/collector_set.test.ts b/src/plugins/usage_collection/server/collector/collector_set.test.ts index 56b4e55eccfc4..43e063b95333e 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.test.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.test.ts @@ -237,6 +237,16 @@ describe('CollectorSet', () => { }, uptime_in_millis: 137844000, }, + process: { + heap: { + total_in_bytes: 1, + used_in_bytes: 2, + size_limit: 3, + }, + resident_set_size_in_bytes: 4, + array_buffers_in_bytes: 5, + external_in_bytes: 6, + }, daysOfTheWeek: ['monday', 'tuesday', 'wednesday'], }; @@ -247,6 +257,16 @@ describe('CollectorSet', () => { memory: { free_bytes: 458280960, total_bytes: 17179869184, used_bytes: 16721588224 }, uptime_ms: 137844000, }, + process: { + heap: { + total_bytes: 1, + used_bytes: 2, + size_limit: 3, + }, + resident_set_size_bytes: 4, + array_buffers_bytes: 5, + external_bytes: 6, + }, days_of_the_week: ['monday', 'tuesday', 'wednesday'], }); }); diff --git a/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts b/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts index 01475fe483c80..54de776faa624 100644 --- a/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts +++ b/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts @@ -504,11 +504,13 @@ describe('saved_visualize_utils', () => { { id: 'wat', image: undefined, + editor: { + editUrl: '/edit/wat', + }, readOnly: false, references: undefined, icon: undefined, savedObjectType: 'visualization', - editUrl: '/edit/wat', type: 'test', typeName: 'test', typeTitle: undefined, diff --git a/src/plugins/visualizations/public/utils/saved_visualize_utils.ts b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts index 9232504e026d3..85932c09729c3 100644 --- a/src/plugins/visualizations/public/utils/saved_visualize_utils.ts +++ b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts @@ -73,7 +73,7 @@ export function mapHitSource( references: SavedObjectReference[]; url: string; savedObjectType?: string; - editUrl?: string; + editor?: { editUrl?: string }; updatedAt?: string; type?: BaseVisType; icon?: BaseVisType['icon']; @@ -108,7 +108,7 @@ export function mapHitSource( newAttributes.icon = newAttributes.type?.icon; newAttributes.image = newAttributes.type?.image; newAttributes.typeTitle = newAttributes.type?.title; - newAttributes.editUrl = `/edit/${id}`; + newAttributes.editor = { editUrl: `/edit/${id}` }; newAttributes.readOnly = Boolean(visTypes.get(typeName as string)?.disableEdit); return newAttributes; @@ -168,7 +168,6 @@ export async function findListItems( return acc; }, acc); }, {} as { [visType: string]: VisualizationsAppExtension }); - const searchOption = (field: string, ...defaults: string[]) => _(extensions).map(field).concat(defaults).compact().flatten().uniq().value() as string[]; diff --git a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts index 2a46b28f06dd9..617f0386f6181 100644 --- a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts +++ b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts @@ -19,8 +19,6 @@ import { BaseVisType } from './base_vis_type'; export type VisualizationStage = 'experimental' | 'beta' | 'production'; export interface VisualizationListItem { - editUrl: string; - editApp?: string; error?: string; icon: string; id: string; @@ -32,6 +30,9 @@ export interface VisualizationListItem { typeTitle: string; image?: string; type?: BaseVisType | string; + editor: + | { editUrl: string; editApp?: string } + | { onEdit: (savedObjectId: string) => Promise }; } export interface SerializableAttributes { @@ -86,8 +87,14 @@ export interface VisualizationsAppExtension { } export interface VisTypeAlias { - aliasPath: string; - aliasApp: string; + /** + * Provide `alias` when your visualization has a dedicated app for creation. + * TODO: Provide a generic callback to create visualizations inline. + */ + alias?: { + app: string; + path: string; + }; name: string; title: string; icon: string; diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.scss b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.scss index 7fe4fe05ab267..48df3fc673886 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.scss +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.scss @@ -11,7 +11,7 @@ .visListingTable__experimentalIcon { width: $euiSizeL; - vertical-align: baseline; + vertical-align: middle; padding: 0 $euiSizeS; margin-left: $euiSizeS; } diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index d4beb45c4e248..d1de28ac73795 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -36,6 +36,7 @@ import { TableListViewProps, } from '@kbn/content-management-table-list-view'; import { TableListViewTable } from '@kbn/content-management-table-list-view-table'; + import { findListItems } from '../../utils/saved_visualize_utils'; import { updateBasicSoAttributes } from '../../utils/saved_objects_utils/update_basic_attributes'; import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title'; @@ -49,17 +50,17 @@ import { getNoItemsMessage, getCustomColumn } from '../utils'; import { getVisualizeListItemLink } from '../utils/get_visualize_list_item_link'; import type { VisualizationStage } from '../../vis_types/vis_type_alias_registry'; -interface VisualizeUserContent extends VisualizationListItem, UserContentCommonSchema { - type: string; - attributes: { - title: string; - description?: string; - editApp: string; - editUrl: string; - readOnly: boolean; - error?: string; +type VisualizeUserContent = VisualizationListItem & + UserContentCommonSchema & { + type: string; + attributes: { + id: string; + title: string; + description?: string; + readOnly: boolean; + error?: string; + }; }; -} const toTableListViewSavedObject = (savedObject: Record): VisualizeUserContent => { return { @@ -67,19 +68,17 @@ const toTableListViewSavedObject = (savedObject: Record): Visua updatedAt: savedObject.updatedAt as string, references: savedObject.references as Array<{ id: string; type: string; name: string }>, type: savedObject.savedObjectType as string, - editUrl: savedObject.editUrl as string, - editApp: savedObject.editApp as string, icon: savedObject.icon as string, stage: savedObject.stage as VisualizationStage, savedObjectType: savedObject.savedObjectType as string, typeTitle: savedObject.typeTitle as string, title: (savedObject.title as string) ?? '', error: (savedObject.error as string) ?? '', + editor: savedObject.editor as any, attributes: { + id: savedObject.id as string, title: (savedObject.title as string) ?? '', description: savedObject.description as string, - editApp: savedObject.editApp as string, - editUrl: savedObject.editUrl as string, readOnly: savedObject.readOnly as boolean, error: savedObject.error as string, }, @@ -120,7 +119,13 @@ const useTableListViewProps = ( }, [closeNewVisModal]); const editItem = useCallback( - ({ attributes: { editUrl, editApp } }: VisualizeUserContent) => { + async ({ attributes: { id }, editor }: VisualizeUserContent) => { + if (!('editApp' in editor || 'editUrl' in editor)) { + await editor.onEdit(id); + return; + } + + const { editApp, editUrl } = editor; if (editApp) { application.navigateToApp(editApp, { path: editUrl }); return; @@ -383,10 +388,19 @@ export const VisualizeListing = () => { entityNamePlural={i18n.translate('visualizations.listing.table.entityNamePlural', { defaultMessage: 'visualizations', })} - getDetailViewLink={({ attributes: { editApp, editUrl, error, readOnly } }) => - readOnly + onClickTitle={(item) => { + tableViewProps.editItem?.(item); + }} + getDetailViewLink={({ editor, attributes: { error, readOnly } }) => + readOnly || (editor && 'onEdit' in editor) ? undefined - : getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl, error) + : getVisualizeListItemLink( + application, + kbnUrlStateStorage, + editor.editApp, + editor.editUrl, + error + ) } tableCaption={visualizeLibraryTitle} {...tableViewProps} diff --git a/src/plugins/visualizations/public/visualize_app/utils/get_visualize_list_item_link.ts b/src/plugins/visualizations/public/visualize_app/utils/get_visualize_list_item_link.ts index 524ccbad44f81..5c1a5b1ee3ebb 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/get_visualize_list_item_link.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/get_visualize_list_item_link.ts @@ -17,10 +17,10 @@ export const getVisualizeListItemLink = ( application: ApplicationStart, kbnUrlStateStorage: IKbnUrlStateStorage, editApp: string | undefined, - editUrl: string, + editUrl: string | undefined, error: string | undefined = undefined ) => { - if (error) { + if (error || (!editApp && !editUrl)) { return undefined; } diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx index e1e9cec25e15a..a9a21446e06a2 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx @@ -37,8 +37,10 @@ describe('GroupSelection', () => { { name: 'visWithAliasUrl', title: 'Vis with alias Url', - aliasApp: 'aliasApp', - aliasPath: '#/aliasApp', + alias: { + app: 'aliasApp', + path: '#/aliasApp', + }, description: 'Vis with alias Url', stage: 'production', group: VisGroups.PROMOTED, @@ -49,8 +51,10 @@ describe('GroupSelection', () => { description: 'Vis alias with promotion', stage: 'production', group: VisGroups.PROMOTED, - aliasApp: 'anotherApp', - aliasPath: '#/anotherUrl', + alias: { + app: 'anotherApp', + path: '#/anotherUrl', + }, promotion: true, } as unknown, ] as BaseVisType[]; diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx index dc8aaa03161b8..3bcdff18c47a7 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx @@ -200,7 +200,7 @@ const VisGroup = ({ visType, onVisTypeSelected }: VisCardProps) => { } onClick={onClick} data-test-subj={`visType-${visType.name}`} - data-vis-stage={!('aliasPath' in visType) ? visType.stage : 'alias'} + data-vis-stage={!('alias' in visType) ? visType.stage : 'alias'} aria-label={`visType-${visType.name}`} description={ <> diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index 907c989c7bb43..0db5f66cc5c92 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -47,8 +47,10 @@ describe('NewVisModal', () => { title: 'Vis with alias Url', stage: 'production', group: VisGroups.PROMOTED, - aliasApp: 'otherApp', - aliasPath: '#/aliasUrl', + alias: { + app: 'otherApp', + path: '#/aliasUrl', + }, }, { name: 'visWithSearch', @@ -181,7 +183,7 @@ describe('NewVisModal', () => { ); }); - it('closes and redirects properly if visualization with aliasPath and originatingApp in props', () => { + it('closes and redirects properly if visualization with alias.path and originatingApp in props', () => { const onClose = jest.fn(); const navigateToApp = jest.fn(); const stateTransfer = embeddablePluginMock.createStartContract().getStateTransfer(); diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index ac1f89e73700f..b1e5de3215260 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -119,7 +119,7 @@ class NewVisModal extends React.Component { - if (!('aliasPath' in visType) && visType.requiresSearch && visType.options.showIndexSelection) { + if ('visConfig' in visType && visType.requiresSearch && visType.options.showIndexSelection) { this.setState({ showSearchVisModal: true, visType, @@ -143,10 +143,12 @@ class NewVisModal extends React.Component { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index 016cead53f0c4..1d0aeeea6a0d9 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -36,13 +36,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const homeUrl = await browser.getCurrentUrl(); // Navigate to discover app - await appsMenu.clickLink('Discover'); + await appsMenu.clickLink('Discover', { category: 'kibana' }); const discoverUrl = await browser.getCurrentUrl(); await PageObjects.timePicker.setDefaultAbsoluteRange(); const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); // Navigate to dashboard app - await appsMenu.clickLink('Dashboard'); + await appsMenu.clickLink('Dashboard', { category: 'kibana' }); // Navigating back to discover await browser.goBack(); diff --git a/test/functional/services/apps_menu.ts b/test/functional/services/apps_menu.ts index 0f63166477883..0dddddd35e77a 100644 --- a/test/functional/services/apps_menu.ts +++ b/test/functional/services/apps_menu.ts @@ -116,23 +116,55 @@ export class AppsMenuService extends FtrService { category, }: { closeCollapsibleNav?: boolean; category?: string } = {} ) { - try { + await this.waitUntilLoadingHasFinished(); + + await this.ctx.getService('retry').try(async () => { this.log.debug(`click "${name}" app link`); - await this.openCollapsibleNav(); - let nav; - if (typeof category === 'string') { - nav = await this.testSubjects.find(`collapsibleNavGroup-${category}`); - } else { - nav = await this.testSubjects.find('collapsibleNav'); - } - const link = await nav.findByPartialLinkText(name); - await link.click(); - if (closeCollapsibleNav) { - await this.closeCollapsibleNav(); + try { + await this.openCollapsibleNav(); + let nav; + if (typeof category === 'string') { + // we can search within a specific section of the side nav + nav = await this.testSubjects.find(`collapsibleNavGroup-${category}`); + const link = await nav.findByPartialLinkText(name); + await link.click(); + } else { + // we need to search our app link in the whole side nav + // first, we get all the links, along with their inner text + const allLinks = await this.testSubjects.findAll('collapsibleNavAppLink'); + const allLinksTexts = await Promise.all( + allLinks.map((link) => link.getVisibleText().then((text) => ({ link, text }))) + ); + + // then, filter out those that don't have a matching text + const matchingLinks = allLinksTexts.filter(({ text }) => text.includes(name)); + if (matchingLinks.length === 0) { + this.log.debug( + `Found ${allLinks.length} links on the side nav: ${allLinksTexts.map( + ({ text }) => text + )}` + ); + throw new Error( + `Could not find the '${name}' application on the side nav (${allLinks.length} links found)` + ); + } else if (matchingLinks.length > 1) { + throw new Error( + `Multiple apps exist in the side nav with the specified name: '${name}'. Consider using the "category" parameter to disambiguate` + ); + } + + this.log.debug(`Found "${name}" app link on the side nav!`); + // if we click on a stale element (e.g. re-rendered) it'll throw an Error and we will retry + await matchingLinks.pop()!.link.click(); + } + + if (closeCollapsibleNav) { + await this.closeCollapsibleNav(); + } + } finally { + // Intentionally empty } - } finally { - // Intentionally empty - } + }); } } diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts index 862cb6acfb6df..d33000bfa70c3 100644 --- a/test/plugin_functional/test_suites/core_plugins/applications.ts +++ b/test/plugin_functional/test_suites/core_plugins/applications.ts @@ -7,7 +7,7 @@ */ import expect from '@kbn/expect'; -import { PluginFunctionalProviderContext } from '../../services'; +import type { PluginFunctionalProviderContext } from '../../services'; export default function ({ getService, getPageObject }: PluginFunctionalProviderContext) { const common = getPageObject('common'); @@ -17,10 +17,24 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider const find = getService('find'); const deployment = getService('deployment'); const esArchiver = getService('esArchiver'); + const log = getService('log'); + + const clickAppLink = async (app: string) => { + const appLink = `fooNav${app}`; + if (!(await testSubjects.exists(appLink))) { + log.debug(`App ${app} not found on side nav`); + } + await testSubjects.click(appLink); + }; const loadingScreenNotShown = async () => expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false); + const checkAppVisible = async (app: string) => { + const appContainer = `fooApp${app}`; + await testSubjects.existOrFail(appContainer); + }; + const getAppWrapperHeight = async () => { const wrapper = await find.byClassName('kbnAppWrapper'); return (await wrapper.getSize()).height; @@ -29,8 +43,7 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider const navigateTo = async (path: string) => await browser.navigateTo(`${deployment.getHostPort()}${path}`); - // FLAKY: https://github.com/elastic/kibana/issues/53356 - describe.skip('ui applications', function describeIndexTests() { + describe('ui applications', function describeIndexTests() { before(async () => { await esArchiver.emptyKibanaIndex(); await common.navigateToApp('foo'); @@ -38,48 +51,48 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider }); it('starts on home page', async () => { - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); }); it('redirects and renders correctly regardless of trailing slash', async () => { await navigateTo(`/app/foo`); await browser.waitForUrlToBe('/app/foo/home'); - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); await navigateTo(`/app/foo/`); await browser.waitForUrlToBe('/app/foo/home'); - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); }); it('navigates to its own pages', async () => { // Go to page A - await testSubjects.click('fooNavPageA'); + await clickAppLink('PageA'); await browser.waitForUrlToBe('/app/foo/page-a'); await loadingScreenNotShown(); - await testSubjects.existOrFail('fooAppPageA'); + await checkAppVisible('PageA'); // Go to home page - await testSubjects.click('fooNavHome'); + await clickAppLink('Home'); await browser.waitForUrlToBe('/app/foo/home'); await loadingScreenNotShown(); - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); }); it('can use the back button to navigate within an app', async () => { await browser.goBack(); await browser.waitForUrlToBe('/app/foo/page-a'); await loadingScreenNotShown(); - await testSubjects.existOrFail('fooAppPageA'); + await checkAppVisible('PageA'); }); it('navigates to app root when navlink is clicked', async () => { - await appsMenu.clickLink('Foo', { category: 'kibana' }); + await appsMenu.clickLink('Foo'); await browser.waitForUrlToBe('/app/foo/home'); await loadingScreenNotShown(); - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); }); it('navigates to other apps', async () => { - await testSubjects.click('fooNavBarPageB'); + await clickAppLink('BarPageB'); await loadingScreenNotShown(); await testSubjects.existOrFail('barAppPageB'); await browser.waitForUrlToBe('/app/bar/page-b?query=here'); @@ -94,7 +107,7 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider await browser.goBack(); await browser.waitForUrlToBe('/app/foo/home'); await loadingScreenNotShown(); - await testSubjects.existOrFail('fooAppHome'); + await checkAppVisible('Home'); }); it('chromeless applications are not visible in apps list', async () => { diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index dc6ff1211eb9e..8b248c5bccc68 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -283,6 +283,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.infra.featureFlags.logThresholdAlertRuleEnabled (any)', 'xpack.infra.featureFlags.logsUIEnabled (any)', 'xpack.infra.featureFlags.alertsAndRulesDropdownEnabled (any)', + 'xpack.infra.featureFlags.profilingEnabled (any)', 'xpack.license_management.ui.enabled (boolean)', 'xpack.maps.preserveDrawingBuffer (boolean)', diff --git a/tsconfig.base.json b/tsconfig.base.json index 2d9419b2b7712..4591e22581156 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -104,6 +104,8 @@ "@kbn/bfetch-plugin/*": ["src/plugins/bfetch/*"], "@kbn/calculate-auto": ["packages/kbn-calculate-auto"], "@kbn/calculate-auto/*": ["packages/kbn-calculate-auto/*"], + "@kbn/calculate-width-from-char-count": ["packages/kbn-calculate-width-from-char-count"], + "@kbn/calculate-width-from-char-count/*": ["packages/kbn-calculate-width-from-char-count/*"], "@kbn/canvas-plugin": ["x-pack/plugins/canvas"], "@kbn/canvas-plugin/*": ["x-pack/plugins/canvas/*"], "@kbn/cases-api-integration-test-plugin": ["x-pack/test/cases_api_integration/common/plugins/cases"], @@ -1026,10 +1028,14 @@ "@kbn/ml-category-validator/*": ["x-pack/packages/ml/category_validator/*"], "@kbn/ml-chi2test": ["x-pack/packages/ml/chi2test"], "@kbn/ml-chi2test/*": ["x-pack/packages/ml/chi2test/*"], + "@kbn/ml-creation-wizard-utils": ["x-pack/packages/ml/creation_wizard_utils"], + "@kbn/ml-creation-wizard-utils/*": ["x-pack/packages/ml/creation_wizard_utils/*"], "@kbn/ml-data-frame-analytics-utils": ["x-pack/packages/ml/data_frame_analytics_utils"], "@kbn/ml-data-frame-analytics-utils/*": ["x-pack/packages/ml/data_frame_analytics_utils/*"], "@kbn/ml-data-grid": ["x-pack/packages/ml/data_grid"], "@kbn/ml-data-grid/*": ["x-pack/packages/ml/data_grid/*"], + "@kbn/ml-data-view-utils": ["x-pack/packages/ml/data_view_utils"], + "@kbn/ml-data-view-utils/*": ["x-pack/packages/ml/data_view_utils/*"], "@kbn/ml-date-picker": ["x-pack/packages/ml/date_picker"], "@kbn/ml-date-picker/*": ["x-pack/packages/ml/date_picker/*"], "@kbn/ml-date-utils": ["x-pack/packages/ml/date_utils"], @@ -1070,6 +1076,8 @@ "@kbn/ml-ui-actions/*": ["x-pack/packages/ml/ui_actions/*"], "@kbn/ml-url-state": ["x-pack/packages/ml/url_state"], "@kbn/ml-url-state/*": ["x-pack/packages/ml/url_state/*"], + "@kbn/mock-idp-plugin": ["packages/kbn-mock-idp-plugin"], + "@kbn/mock-idp-plugin/*": ["packages/kbn-mock-idp-plugin/*"], "@kbn/monaco": ["packages/kbn-monaco"], "@kbn/monaco/*": ["packages/kbn-monaco/*"], "@kbn/monitoring-collection-plugin": ["x-pack/plugins/monitoring_collection"], @@ -1108,6 +1116,8 @@ "@kbn/oidc-provider-plugin/*": ["x-pack/test/security_api_integration/plugins/oidc_provider/*"], "@kbn/open-telemetry-instrumented-plugin": ["test/common/plugins/otel_metrics"], "@kbn/open-telemetry-instrumented-plugin/*": ["test/common/plugins/otel_metrics/*"], + "@kbn/openapi-bundler": ["packages/kbn-openapi-bundler"], + "@kbn/openapi-bundler/*": ["packages/kbn-openapi-bundler/*"], "@kbn/openapi-generator": ["packages/kbn-openapi-generator"], "@kbn/openapi-generator/*": ["packages/kbn-openapi-generator/*"], "@kbn/optimizer": ["packages/kbn-optimizer"], @@ -1122,6 +1132,8 @@ "@kbn/paertial-results-example-plugin/*": ["examples/partial_results_example/*"], "@kbn/painless-lab-plugin": ["x-pack/plugins/painless_lab"], "@kbn/painless-lab-plugin/*": ["x-pack/plugins/painless_lab/*"], + "@kbn/panel-loader": ["packages/kbn-panel-loader"], + "@kbn/panel-loader/*": ["packages/kbn-panel-loader/*"], "@kbn/peggy": ["packages/kbn-peggy"], "@kbn/peggy/*": ["packages/kbn-peggy/*"], "@kbn/peggy-loader": ["packages/kbn-peggy-loader"], @@ -1278,6 +1290,12 @@ "@kbn/security-api-integration-helpers/*": ["x-pack/test/security_api_integration/packages/helpers/*"], "@kbn/security-plugin": ["x-pack/plugins/security"], "@kbn/security-plugin/*": ["x-pack/plugins/security/*"], + "@kbn/security-plugin-types-common": ["x-pack/packages/security/plugin_types_common"], + "@kbn/security-plugin-types-common/*": ["x-pack/packages/security/plugin_types_common/*"], + "@kbn/security-plugin-types-public": ["x-pack/packages/security/plugin_types_public"], + "@kbn/security-plugin-types-public/*": ["x-pack/packages/security/plugin_types_public/*"], + "@kbn/security-plugin-types-server": ["x-pack/packages/security/plugin_types_server"], + "@kbn/security-plugin-types-server/*": ["x-pack/packages/security/plugin_types_server/*"], "@kbn/security-solution-ess": ["x-pack/plugins/security_solution_ess"], "@kbn/security-solution-ess/*": ["x-pack/plugins/security_solution_ess/*"], "@kbn/security-solution-features": ["x-pack/packages/security-solution/features"], @@ -1498,8 +1516,6 @@ "@kbn/stdio-dev-helpers/*": ["packages/kbn-stdio-dev-helpers/*"], "@kbn/storybook": ["packages/kbn-storybook"], "@kbn/storybook/*": ["packages/kbn-storybook/*"], - "@kbn/subscription-tracking": ["packages/kbn-subscription-tracking"], - "@kbn/subscription-tracking/*": ["packages/kbn-subscription-tracking/*"], "@kbn/synthetics-plugin": ["x-pack/plugins/synthetics"], "@kbn/synthetics-plugin/*": ["x-pack/plugins/synthetics/*"], "@kbn/task-manager-fixture-plugin": ["x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture"], @@ -1678,9 +1694,7 @@ "@kbn/zod-helpers/*": ["packages/kbn-zod-helpers/*"], // END AUTOMATED PACKAGE LISTING // Allows for importing from `kibana` package for the exported types. - "@emotion/core": [ - "typings/@emotion" - ], + "@emotion/core": ["typings/@emotion"] }, // Support .tsx files and transform JSX into calls to React.createElement "jsx": "react", @@ -1751,6 +1765,6 @@ "@kbn/ambient-ui-types", "@kbn/ambient-common-types", "@kbn/ambient-storybook-types" - ], + ] } } diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index bb14c06a53e36..3a6b3bc95a812 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -55,10 +55,13 @@ "xpack.metricsData": "plugins/metrics_data_access", "xpack.ml": [ "packages/ml/anomaly_utils", + "packages/ml/creation_wizard_utils", "packages/ml/data_grid", + "packages/ml/data_view_utils", "packages/ml/date_picker", "packages/ml/trained_models_utils", "packages/ml/category_validator", + "packages/ml/ui_actions", "plugins/ml" ], "xpack.monitoring": ["plugins/monitoring"], diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx index b8ee12525c68a..c38983df5a6cc 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx @@ -319,6 +319,9 @@ describe('API tests', () => { evalParams: { agents: ['not', 'alphabetical'], dataset: '{}', + datasetName: 'Test Dataset', + projectName: 'Test Project Name', + runName: 'Test Run Name', evalModel: ['not', 'alphabetical'], evalPrompt: 'evalPrompt', evaluationType: ['not', 'alphabetical'], @@ -336,9 +339,12 @@ describe('API tests', () => { query: { models: 'alphabetical,not', agents: 'alphabetical,not', + datasetName: 'Test Dataset', evaluationType: 'alphabetical,not', evalModel: 'alphabetical,not', outputIndex: 'outputIndex', + projectName: 'Test Project Name', + runName: 'Test Run Name', }, signal: undefined, }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx index f92585cbdd011..a0263bc21b2da 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx @@ -26,6 +26,10 @@ export interface FetchConnectorExecuteResponse { response: string | ReadableStreamDefaultReader; isError: boolean; isStream: boolean; + traceData?: { + transactionId: string; + traceId: string; + }; } export const fetchConnectorExecuteAction = async ({ @@ -112,6 +116,10 @@ export const fetchConnectorExecuteAction = async ({ status: string; data: string; service_message?: string; + trace_data?: { + transaction_id: string; + trace_id: string; + }; }>(`/internal/elastic_assistant/actions/connector/${apiConfig?.connectorId}/_execute`, { method: 'POST', body: JSON.stringify(requestBody), @@ -133,10 +141,21 @@ export const fetchConnectorExecuteAction = async ({ isStream: false, }; } + + // Only add traceData if it exists in the response + const traceData = + response.trace_data?.trace_id != null && response.trace_data?.transaction_id != null + ? { + traceId: response.trace_data?.trace_id, + transactionId: response.trace_data?.transaction_id, + } + : undefined; + return { response: assistantLangChain ? getFormattedMessageContent(response.data) : response.data, isError: false, isStream: false, + traceData, }; } catch (error) { const reader = error?.response?.body?.getReader(); @@ -281,6 +300,7 @@ export interface PostEvaluationParams { } export interface PostEvaluationResponse { + evaluationId: string; success: boolean; } @@ -302,11 +322,14 @@ export const postEvaluation = async ({ try { const path = `/internal/elastic_assistant/evaluate`; const query = { - models: evalParams?.models.sort()?.join(','), agents: evalParams?.agents.sort()?.join(','), + datasetName: evalParams?.datasetName, evaluationType: evalParams?.evaluationType.sort()?.join(','), evalModel: evalParams?.evalModel.sort()?.join(','), outputIndex: evalParams?.outputIndex, + models: evalParams?.models.sort()?.join(','), + projectName: evalParams?.projectName, + runName: evalParams?.runName, }; const response = await http.fetch(path, { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts index 61001d95e8a3e..fa9617d00a6e1 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts @@ -22,6 +22,7 @@ export const getMessageFromRawResponse = (rawResponse: FetchConnectorExecuteResp : { content: response as string }), timestamp: dateTimeString, isError, + traceData: rawResponse.traceData, }; } else { return { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx index ab8d79454b0e3..f4fe4d7f8a407 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { + EuiAccordion, euiPaletteComplementary, EuiFormRow, EuiTitle, @@ -17,12 +18,14 @@ import { EuiButton, EuiComboBoxOptionOption, EuiTextArea, + EuiTextColor, EuiFieldText, EuiFlexItem, EuiFlexGroup, EuiLink, } from '@elastic/eui'; +import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import * as i18n from './translations'; import { useAssistantContext } from '../../../assistant_context'; @@ -30,6 +33,8 @@ import { useLoadConnectors } from '../../../connectorland/use_load_connectors'; import { getActionTypeTitle, getGenAiConfig } from '../../../connectorland/helpers'; import { PRECONFIGURED_CONNECTOR } from '../../../connectorland/translations'; import { usePerformEvaluation } from './use_perform_evaluation'; +import { getApmLink, getDiscoverLink } from './utils'; +import { PostEvaluationResponse } from '../../api'; /** * See AGENT_EXECUTOR_MAP in `x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts` @@ -41,6 +46,7 @@ const DEFAULT_EVAL_TYPES_OPTIONS = [ { label: 'esql-validator', disabled: true }, { label: 'custom', disabled: true }, ]; +const DEFAULT_OUTPUT_INDEX = '.kibana-elastic-ai-assistant-evaluation-results'; interface Props { onEvaluationSettingsChange?: () => void; @@ -52,10 +58,93 @@ interface Props { export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSettingsChange }) => { const { actionTypeRegistry, basePath, http } = useAssistantContext(); const { data: connectors } = useLoadConnectors({ http }); - const { mutate: performEvaluation, isLoading: isPerformingEvaluation } = usePerformEvaluation({ + const { + data: evalResponse, + mutate: performEvaluation, + isLoading: isPerformingEvaluation, + } = usePerformEvaluation({ http, }); + // Run Details + // Project Name + const [projectName, setProjectName] = useState(); + const onProjectNameChange = useCallback( + (e) => { + setProjectName(e.target.value); + }, + [setProjectName] + ); + // Run Name + const [runName, setRunName] = useState(); + const onRunNameChange = useCallback( + (e) => { + setRunName(e.target.value); + }, + [setRunName] + ); + // Local Output Index + const [outputIndex, setOutputIndex] = useState(DEFAULT_OUTPUT_INDEX); + const onOutputIndexChange = useCallback( + (e) => { + setOutputIndex(e.target.value); + }, + [setOutputIndex] + ); + // Dataset + const [useLangSmithDataset, setUseLangSmithDataset] = useState(true); + const datasetToggleButton = useMemo(() => { + return ( + + {i18n.EVALUATOR_DATASET_LABEL} + {' ('} + setUseLangSmithDataset(true)} + > + {i18n.LANGSMITH_DATASET_LABEL} + + {' / '} + setUseLangSmithDataset(false)} + > + {i18n.CUSTOM_DATASET_LABEL} + + {')'} + + ); + }, [useLangSmithDataset]); + const [datasetName, setDatasetName] = useState(); + const onDatasetNameChange = useCallback( + (e) => { + setDatasetName(e.target.value); + }, + [setDatasetName] + ); + const sampleDataset = [ + { + input: + 'As an expert user of Elastic Security, please generate an accurate and valid ESQL query to detect the use case below. Your response should be formatted to be able to use immediately in an Elastic Security timeline or detection rule. Take your time with the answer, and really make sure you check your knowledge really well on all the functions I am asking for. check it multiple times if you need to. I cannot afford for queries to be inaccurate. Assume I am using the Elastic Common Schema. Ensure the answers are formatted in a way which is easily copyable.\n\n' + + 'Write an ESQL query for detecting cryptomining activity on an AWS EC2 instance.', + reference: + 'FROM metrics-apm*\n| WHERE metricset.name == ""transaction"" AND metricset.interval == ""1m""\n| EVAL bucket = AUTO_BUCKET(transaction.duration.histogram, 50, , )\n| STATS avg_duration = AVG(transaction.duration.histogram) BY bucket', + }, + ]; + const [datasetText, setDatasetText] = useState(JSON.stringify(sampleDataset, null, 2)); + const onDatasetTextChange = useCallback( + (e) => { + setDatasetText(e.target.value); + }, + [setDatasetText] + ); + + // Predictions // Connectors / Models const [selectedModelOptions, setSelectedModelOptions] = useState< Array> @@ -109,6 +198,7 @@ export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSet return DEFAULT_AGENTS.map((label) => ({ label })); }, []); + // Evaluation // Evaluation Type const [selectedEvaluationType, setSelectedEvaluationType] = useState< Array> @@ -146,15 +236,6 @@ export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSet [setSelectedEvaluatorModelOptions] ); - // Output Index - const [outputIndex, setOutputIndex] = useState('.kibana-elastic-ai-assistant-evaluation-results'); - const onOutputIndexChange = useCallback( - (e) => { - setOutputIndex(e.target.value); - }, - [setOutputIndex] - ); - // Eval Prompt const sampleEvalPrompt: string = `For the below input: \n\n{{input}} \n\na prediction: \n\n{{prediction}} \n\nwas made. How's it stack up against this reference: \n\n{{reference}} \n\nReturn output in a succinct sentence ranking on a simple grading rubric focused on correctness.`; const [evalPrompt, setEvalPrompt] = useState(sampleEvalPrompt); @@ -165,55 +246,88 @@ export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSet [setEvalPrompt] ); - // Dataset - const sampleDataset = [ - { - input: - 'I want to see a query for metrics-apm*, filtering on metricset.name:transaction and metricset.interval:1m, showing the average duration (via transaction.duration.histogram), in 50 buckets. Only return the ESQL query, and do not wrap in a codeblock.', - reference: - 'FROM metrics-apm*\n| WHERE metricset.name == ""transaction"" AND metricset.interval == ""1m""\n| EVAL bucket = AUTO_BUCKET(transaction.duration.histogram, 50, , )\n| STATS avg_duration = AVG(transaction.duration.histogram) BY bucket', - }, - ]; - const [datasetText, setDatasetText] = useState(JSON.stringify(sampleDataset, null, 2)); - const onDatasetTextChange = useCallback( - (e) => { - setDatasetText(e.target.value); - }, - [setDatasetText] - ); - + // Required fields by eval API const isPerformEvaluationDisabled = selectedModelOptions.length === 0 || selectedAgentOptions.length === 0 || - selectedEvaluatorModelOptions.length === 0 || - selectedEvaluationType.length === 0 || - datasetText.length === 0 || outputIndex.length === 0; // Perform Evaluation Button - const handlePerformEvaluation = useCallback(() => { + const handlePerformEvaluation = useCallback(async () => { const evalParams = { models: selectedModelOptions.flatMap((option) => option.key ?? []), agents: selectedAgentOptions.map((option) => option.label), - dataset: datasetText, + dataset: useLangSmithDataset ? undefined : datasetText, + datasetName: useLangSmithDataset ? datasetName : undefined, evalModel: selectedEvaluatorModelOptions.flatMap((option) => option.key ?? []), evalPrompt, evaluationType: selectedEvaluationType.map((option) => option.label), outputIndex, + projectName, + runName, }; performEvaluation(evalParams); }, [ + datasetName, datasetText, evalPrompt, outputIndex, performEvaluation, + projectName, + runName, selectedAgentOptions, selectedEvaluationType, selectedEvaluatorModelOptions, selectedModelOptions, + useLangSmithDataset, ]); - const discoverLink = `${basePath}/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:now-7d%2Fd,to:now))&_a=(columns:!('@timestamp',evaluationId,totalAgents,totalInput,totalRequests,input,reference,prediction,evaluation.value,evaluation.reasoning,predictionResponse.value.connector_id),filters:!(),grid:(columns:('@timestamp':(width:212),evaluationId:(width:285),totalAgents:(width:111),totalInput:(width:98),totalRequests:(width:121))),index:'6d9ba861-a76b-4d31-90f4-dfb8f01b78bd',interval:auto,query:(esql:'from%20.kibana-elastic-ai-assistant-evaluation-results%20%0A%7C%20keep%20@timestamp,%20evaluationId,%20totalAgents,%20totalInput,%20totalRequests,%20input,%20reference,%20prediction,%20evaluation.value,%20evaluation.reasoning,%20predictionResponse.value.connector_id%0A%7C%20sort%20@timestamp%20desc%0A%7C%20limit%20100%0A%0A%0A'),sort:!(!('@timestamp',desc)))`; + const discoverLink = useMemo( + () => getDiscoverLink(basePath, (evalResponse as PostEvaluationResponse)?.evaluationId ?? ''), + [basePath, evalResponse] + ); + + const apmLink = useMemo( + () => getApmLink(basePath, (evalResponse as PostEvaluationResponse)?.evaluationId ?? ''), + [basePath, evalResponse] + ); + + const getSection = (title: string, description: string) => ( +
    + + + +

    {title}

    +
    +
    +
    + + +

    + {description} +

    +
    +
    + ); + + const runDetailsSection = useMemo( + () => getSection(i18n.RUN_DETAILS_TITLE, i18n.RUN_DETAILS_DESCRIPTION), + [] + ); + const predictionDetailsSection = useMemo( + () => getSection(i18n.PREDICTION_DETAILS_TITLE, i18n.PREDICTION_DETAILS_DESCRIPTION), + [] + ); + const evalDetailsSection = useMemo( + () => getSection(i18n.EVALUATION_DETAILS_TITLE, i18n.EVALUATION_DETAILS_DESCRIPTION), + [] + ); + + const buttonCss = css` + &:hover { + text-decoration: none; + } + `; return ( <> @@ -223,113 +337,193 @@ export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSet {i18n.SETTINGS_DESCRIPTION} - - - - - - - - - - + + + + + + + + + + + + + {useLangSmithDataset ? ( + + ) : ( + + )} + + + + + + + {/* Prediction Details*/} + - - + + + - + + + + + {/* Evaluation Details*/} + - - + + + - - - + + + - - - - - - - - + helpText={i18n.EVALUATION_PROMPT_DESCRIPTION} + > + + + - = React.memo(({ onEvaluationSet {i18n.EVALUATOR_FUN_FACT_DISCOVER_LINK} ), + apm: ( + + {i18n.EVALUATOR_FUN_FACT_APM_LINK} + + ), }} /> - ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts index d0299cb7d5139..305090d766788 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/translations.ts @@ -17,7 +17,93 @@ export const SETTINGS_DESCRIPTION = i18n.translate( 'xpack.elasticAssistant.assistant.settings.evaluationSettings.settingsDescription', { defaultMessage: - 'Not-so-secret dev UI for evaluating sample datasets against models/agents/more...', + 'Run predictions and evaluations against test data sets using different models (connectors), agents, and evaluation schemes.', + } +); + +export const RUN_DETAILS_TITLE = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.runDetailsTitle', + { + defaultMessage: '🏃 Run Details', + } +); + +export const RUN_DETAILS_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.runDetailsDescription', + { + defaultMessage: 'Configure test run details like project, run name, dataset, and output index', + } +); + +export const PREDICTION_DETAILS_TITLE = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.predictionDetailsTitle', + { + defaultMessage: '🔮 Predictions', + } +); + +export const PREDICTION_DETAILS_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.predictionDetailsDescription', + { + defaultMessage: + 'Choose models (connectors) and corresponding agents the dataset should run against', + } +); + +export const EVALUATION_DETAILS_TITLE = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationDetailsTitle', + { + defaultMessage: '🧮 Evaluation (Optional)', + } +); + +export const EVALUATION_DETAILS_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationDetailsDescription', + { + defaultMessage: + 'Evaluate prediction results using a specific model (connector) and evaluation criterion', + } +); + +export const PROJECT_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.projectLabel', + { + defaultMessage: 'Project', + } +); + +export const PROJECT_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.projectDescription', + { + defaultMessage: 'LangSmith project to write results to', + } +); + +export const PROJECT_PLACEHOLDER = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.projectPlaceholder', + { + defaultMessage: '8.12 Testing', + } +); + +export const RUN_NAME_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.runNameLabel', + { + defaultMessage: 'Run name', + } +); + +export const RUN_NAME_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.runNameDescription', + { + defaultMessage: 'Name for this specific test run', + } +); + +export const RUN_NAME_PLACEHOLDER = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.runNamePlaceholder', + { + defaultMessage: '8.12 ESQL Query Generation', } ); @@ -114,11 +200,39 @@ export const EVALUATOR_DATASET_LABEL = i18n.translate( } ); -export const EVALUATOR_DATASET_DESCRIPTION = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetDescription', +export const LANGSMITH_DATASET_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.langsmithDatasetLabel', + { + defaultMessage: 'LangSmith', + } +); + +export const LANGSMITH_DATASET_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.langsmithDatasetDescription', + { + defaultMessage: 'Name of dataset hosted on LangSmith to evaluate', + } +); + +export const LANGSMITH_DATASET_PLACEHOLDER = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.langsmithDatasetPlaceholder', + { + defaultMessage: 'ESQL Query Generation', + } +); + +export const CUSTOM_DATASET_LABEL = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.customDatasetLabel', + { + defaultMessage: 'Custom', + } +); + +export const CUSTOM_DATASET_DESCRIPTION = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.customDatasetDescription', { defaultMessage: - 'Sample data set to evaluate. Array of objects with "input" and "references" properties', + 'Custom dataset to evaluate. Array of objects with "input" and "references" properties', } ); @@ -132,6 +246,12 @@ export const PERFORM_EVALUATION = i18n.translate( export const EVALUATOR_FUN_FACT_DISCOVER_LINK = i18n.translate( 'xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactDiscoverLinkText', { - defaultMessage: 'click here', + defaultMessage: 'Discover', + } +); +export const EVALUATOR_FUN_FACT_APM_LINK = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactApmLinkText', + { + defaultMessage: 'APM', } ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/use_perform_evaluation.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/use_perform_evaluation.tsx index ac194d9c02256..158f7159310ad 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/use_perform_evaluation.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/use_perform_evaluation.tsx @@ -20,12 +20,15 @@ export interface UsePerformEvaluationParams { export interface PerformEvaluationParams { agents: string[]; - dataset: string; + dataset: string | undefined; + datasetName: string | undefined; evalModel: string[]; evalPrompt: string; evaluationType: string[]; models: string[]; outputIndex: string; + projectName: string | undefined; + runName: string | undefined; } /** diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/utils.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/utils.tsx new file mode 100644 index 0000000000000..ae35db921b075 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/utils.tsx @@ -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. + */ + +/** + * Link to Discover for viewing an evaluation + * + * @param basePath + * @param evaluationId + */ +export const getDiscoverLink = (basePath: string, evaluationId: string) => { + return `${basePath}/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:now-1y%2Fd,to:now))&_a=(columns:!(evaluationId,runName,totalAgents,totalInput,totalRequests,input,reference,prediction,evaluation.value,evaluation.reasoning,connectorName,connectorName.keyword,evaluation.__run.runId,evaluation.__run.runId.keyword,evaluation.score,evaluationEnd,evaluationId.keyword,evaluationStart,input.keyword,inputExampleId,inputExampleId.keyword,evaluationDuration,prediction.keyword,predictionResponse.reason.sendToLLM,predictionResponse.status,ConnectorId,predictionResponse.value.data,predictionResponse.value.data.keyword,predictionResponse.value.status,predictionResponse.value.trace_data.trace_id,predictionResponse.value.trace_data.trace_id.keyword,predictionResponse.value.trace_data.transaction_id,predictionResponse.value.trace_data.transaction_id.keyword,reference.keyword,runName.keyword),filters:!(),grid:(columns:('@timestamp':(width:212),ConnectorId:(width:133),connectorName:(width:181),connectorName.keyword:(width:229),evaluation.__run.runId:(width:282),evaluation.__run.runId.keyword:(width:245),evaluation.reasoning:(width:336),evaluation.reasoning.keyword:(width:232),evaluation.score:(width:209),evaluation.value:(width:156),evaluationDuration:(width:174),evaluationEnd:(width:151),evaluationId:(width:130),evaluationId.keyword:(width:186),evaluationStart:(width:202),input:(width:347),input.keyword:(width:458),prediction:(width:264),prediction.keyword:(width:313),predictionResponse.value.connector_id:(width:294),predictionResponse.value.trace_data.trace_id:(width:278),predictionResponse.value.trace_data.transaction_id.keyword:(width:177),reference:(width:305),reference.keyword:(width:219),runName:(width:405),totalAgents:(width:125),totalInput:(width:111),totalRequests:(width:138))),hideChart:!t,index:ce1b41cb-6298-4612-a33c-ba85b3c18ec7,interval:auto,query:(esql:'from%20.kibana-elastic-ai-assistant-evaluation-results%20%0A%7C%20keep%20@timestamp,%20evaluationId,%20runName,%20totalAgents,%20totalInput,%20totalRequests,%20input,%20reference,%20prediction,%20evaluation.value,%20evaluation.reasoning,%20connectorName,%20*%0A%7C%20drop%20evaluation.reasoning.keyword%0A%7C%20rename%20predictionResponse.value.connector_id%20as%20ConnectorId%0A%7C%20where%20evaluationId%20%3D%3D%20%22${evaluationId}%22%0A%7C%20sort%20@timestamp%20desc%0A%7C%20limit%20100%0A%0A%0A'),rowHeight:15,sort:!(!('@timestamp',desc)))`; +}; + +/** + * Link to APM Trace Explorer for viewing an evaluation + * @param basePath + * @param evaluationId + */ +export const getApmLink = (basePath: string, evaluationId: string) => { + return `${basePath}/app/apm/traces/explorer/waterfall?comparisonEnabled=false&detailTab=timeline&environment=ENVIRONMENT_ALL&kuery=&query=%22labels.evaluationId%22:%20%22${evaluationId}%22&rangeFrom=now-1y&rangeTo=now&showCriticalPath=false&traceId=451662121b1f5e6c44084ad7415b9409&transactionId=5f1392fa04766025&type=kql&waterfallItemId=`; +}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx index f9ade4b9abc1e..d4d2d1672b2ca 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx @@ -20,6 +20,10 @@ export interface Message { timestamp: string; isError?: boolean; presentation?: MessagePresentation; + traceData?: { + transactionId: string; + traceId: string; + }; } export interface ConversationTheme { diff --git a/x-pack/packages/ml/aiops_utils/window_parameters.ts b/x-pack/packages/ml/aiops_utils/window_parameters.ts index 806d224defdf4..91ecee5f841a2 100644 --- a/x-pack/packages/ml/aiops_utils/window_parameters.ts +++ b/x-pack/packages/ml/aiops_utils/window_parameters.ts @@ -15,25 +15,13 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object'; * @typedef {WindowParameters} */ export interface WindowParameters { - /** - * Baseline minimum value - * @type {number} - */ + /** Baseline minimum value */ baselineMin: number; - /** - * Baseline maximum value - * @type {number} - */ + /** Baseline maximum value */ baselineMax: number; - /** - * Deviation minimum value - * @type {number} - */ + /** Deviation minimum value */ deviationMin: number; - /** - * Deviation maximum value - * @type {number} - */ + /** Deviation maximum value */ deviationMax: number; } diff --git a/x-pack/packages/ml/creation_wizard_utils/README.md b/x-pack/packages/ml/creation_wizard_utils/README.md new file mode 100644 index 0000000000000..b638cc6f85ca1 --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-creation-wizard-utils + +Utilities and components shared across creation wizards in plugins owned by the @elastic/ml-ui team. diff --git a/x-pack/packages/ml/creation_wizard_utils/components/destination_index_form.tsx b/x-pack/packages/ml/creation_wizard_utils/components/destination_index_form.tsx new file mode 100644 index 0000000000000..057969112a61f --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/components/destination_index_form.tsx @@ -0,0 +1,102 @@ +/* + * Copyright 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, { type FC } from 'react'; + +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { UseIdAsIndexNameSwitch } from './use_id_as_index_name_switch'; + +interface DestinationIndexFormProps { + createIndexLink: string; + destinationIndex: string; + destinationIndexNameEmpty: boolean; + destinationIndexNameExists: boolean; + destinationIndexNameValid: boolean; + destIndexSameAsId: boolean; + fullWidth?: boolean; + indexNameExistsMessage: string; + isJobCreated: boolean; + onDestinationIndexChange: (d: string) => void; + setDestIndexSameAsId: (d: boolean) => void; + switchLabel: string; +} + +export const DestinationIndexForm: FC = ({ + createIndexLink, + destinationIndex, + destinationIndexNameEmpty, + destinationIndexNameExists, + destinationIndexNameValid, + destIndexSameAsId, + fullWidth = true, + indexNameExistsMessage, + isJobCreated, + onDestinationIndexChange, + setDestIndexSameAsId, + switchLabel, +}) => ( + <> + + + + {destIndexSameAsId === false && ( + + {i18n.translate('xpack.ml.creationWizardUtils.destinationIndexInvalidError', { + defaultMessage: 'Invalid destination index name.', + })} +
    + + {i18n.translate('xpack.ml.creationWizardUtils.destinationIndexInvalidErrorLink', { + defaultMessage: 'Learn more about index name limitations.', + })} + + , + ] + } + > + onDestinationIndexChange(e.target.value)} + aria-label={i18n.translate( + 'xpack.ml.creationWizardUtils.destinationIndexInputAriaLabel', + { + defaultMessage: 'Choose a unique destination index name.', + } + )} + isInvalid={!destinationIndexNameEmpty && !destinationIndexNameValid} + data-test-subj="mlCreationWizardUtilsDestinationIndexInput" + /> +
    + )} + +); diff --git a/x-pack/packages/ml/creation_wizard_utils/components/use_id_as_index_name_switch.tsx b/x-pack/packages/ml/creation_wizard_utils/components/use_id_as_index_name_switch.tsx new file mode 100644 index 0000000000000..1191ffec55017 --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/components/use_id_as_index_name_switch.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type FC } from 'react'; + +import { EuiSwitch } from '@elastic/eui'; + +interface UseIdAsIndexNameSwitchProps { + destIndexSameAsId: boolean; + isJobCreated: boolean; + setDestIndexSameAsId: (d: boolean) => void; + label: string; +} + +export const UseIdAsIndexNameSwitch: FC = ({ + destIndexSameAsId, + isJobCreated, + setDestIndexSameAsId, + label, +}) => ( + setDestIndexSameAsId(!destIndexSameAsId)} + data-test-subj="mlCreationWizardUtilsJobIdAsDestIndexNameSwitch" + /> +); diff --git a/x-pack/packages/ml/creation_wizard_utils/jest.config.js b/x-pack/packages/ml/creation_wizard_utils/jest.config.js new file mode 100644 index 0000000000000..04abfa6eec19c --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/creation_wizard_utils'], +}; diff --git a/x-pack/packages/ml/creation_wizard_utils/kibana.jsonc b/x-pack/packages/ml/creation_wizard_utils/kibana.jsonc new file mode 100644 index 0000000000000..158d811883500 --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-creation-wizard-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/creation_wizard_utils/package.json b/x-pack/packages/ml/creation_wizard_utils/package.json new file mode 100644 index 0000000000000..c48eab9641bde --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-creation-wizard-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/packages/ml/creation_wizard_utils/tsconfig.json b/x-pack/packages/ml/creation_wizard_utils/tsconfig.json new file mode 100644 index 0000000000000..df10dcf5b4b50 --- /dev/null +++ b/x-pack/packages/ml/creation_wizard_utils/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n", + ] +} diff --git a/x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts b/x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts index 16c07a57e2c91..3b2e540730fa1 100644 --- a/x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts +++ b/x-pack/packages/ml/data_frame_analytics_utils/src/constants.ts @@ -36,6 +36,7 @@ export const DEFAULT_RESULTS_FIELD = 'ml'; */ export const JOB_MAP_NODE_TYPES = { ANALYTICS: 'analytics', + ANALYTICS_JOB_MISSING: 'analytics-job-missing', TRANSFORM: 'transform', INDEX: 'index', TRAINED_MODEL: 'trainedModel', diff --git a/x-pack/packages/ml/data_grid/index.ts b/x-pack/packages/ml/data_grid/index.ts index 129feadb00ca8..49468ce79c513 100644 --- a/x-pack/packages/ml/data_grid/index.ts +++ b/x-pack/packages/ml/data_grid/index.ts @@ -10,7 +10,7 @@ export { getDataGridSchemaFromESFieldType, getDataGridSchemaFromKibanaFieldType, getFeatureImportance, - getFieldsFromKibanaIndexPattern, + getFieldsFromKibanaDataView, getNestedOrEscapedVal, getProcessedFields, getTopClasses, diff --git a/x-pack/packages/ml/data_grid/lib/common.ts b/x-pack/packages/ml/data_grid/lib/common.ts index 3b6e8dc4dad60..84dc4591d9a10 100644 --- a/x-pack/packages/ml/data_grid/lib/common.ts +++ b/x-pack/packages/ml/data_grid/lib/common.ts @@ -83,7 +83,7 @@ export const euiDataGridToolbarSettings = { * @param {DataView} dataView - The Kibana data view. * @returns {string[]} - The array of field names from the data view. */ -export const getFieldsFromKibanaIndexPattern = (dataView: DataView): string[] => { +export const getFieldsFromKibanaDataView = (dataView: DataView): string[] => { const allFields = dataView.fields.map((f) => f.name); const dataViewFields: string[] = allFields.filter((f) => { if (dataView.metaFields.includes(f)) { diff --git a/x-pack/packages/ml/data_grid/lib/types.ts b/x-pack/packages/ml/data_grid/lib/types.ts index 2c1ab284d1c20..2d8ed5319237d 100644 --- a/x-pack/packages/ml/data_grid/lib/types.ts +++ b/x-pack/packages/ml/data_grid/lib/types.ts @@ -158,9 +158,9 @@ export interface UseIndexDataReturnType */ renderCellValue: RenderCellValue; /** - * Optional index pattern fields. + * Optional data view fields. */ - indexPatternFields?: string[]; + dataViewFields?: string[]; /** * Optional time range. */ diff --git a/x-pack/packages/ml/data_view_utils/README.md b/x-pack/packages/ml/data_view_utils/README.md new file mode 100644 index 0000000000000..33b0a8bae4f8f --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-data-view-utils + +Utilities for handling data views in plugins owned by the @elastic/ml-ui team. diff --git a/x-pack/packages/ml/data_view_utils/actions/create.ts b/x-pack/packages/ml/data_view_utils/actions/create.ts new file mode 100644 index 0000000000000..56d6acbeb0d2c --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/actions/create.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 type { DataViewsService, RuntimeField } from '@kbn/data-views-plugin/common'; + +import type { CreateDataViewApiResponseSchema } from '../types/api_create_response_schema'; + +interface CreateDataViewFnOptions { + dataViewsService: DataViewsService; + dataViewName: string; + runtimeMappings: Record; + timeFieldName?: string; + errorFallbackId: string; +} + +export const createDataViewFn = async ({ + dataViewsService, + dataViewName, + runtimeMappings, + timeFieldName, + // A fall back id to be able to track the response + // because in case of an error we don't get a data view id. + errorFallbackId, +}: CreateDataViewFnOptions): Promise => { + const response: CreateDataViewApiResponseSchema = { + dataViewsCreated: [], + dataViewsErrors: [], + }; + + try { + const dataViewsResp = await dataViewsService.createAndSave( + { + title: dataViewName, + timeFieldName, + runtimeFieldMap: runtimeMappings, + allowNoIndex: true, + }, + false, + true + ); + + if (dataViewsResp.id) { + response.dataViewsCreated = [{ id: dataViewsResp.id }]; + } + } catch (error) { + response.dataViewsErrors = [{ id: errorFallbackId, error }]; + } + + return response; +}; diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts b/x-pack/packages/ml/data_view_utils/actions/data_view_handler.ts similarity index 93% rename from x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts rename to x-pack/packages/ml/data_view_utils/actions/data_view_handler.ts index fc07c69d8dc73..97ec5cbf97665 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts +++ b/x-pack/packages/ml/data_view_utils/actions/data_view_handler.ts @@ -9,7 +9,7 @@ import { DataViewsService } from '@kbn/data-views-plugin/common'; export class DataViewHandler { constructor(private dataViewService: DataViewsService) {} - // returns a id based on an index pattern name + // returns a id based on an data view name async getDataViewId(indexName: string) { const dv = (await this.dataViewService.find(indexName)).find( ({ title }) => title === indexName diff --git a/x-pack/packages/ml/data_view_utils/actions/delete.ts b/x-pack/packages/ml/data_view_utils/actions/delete.ts new file mode 100644 index 0000000000000..3ae6429d3c124 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/actions/delete.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataViewsService } from '@kbn/data-views-plugin/common'; + +import type { DeleteDataViewApiResponseSchema } from '../types/api_delete_response_schema'; + +import { DataViewHandler } from './data_view_handler'; + +async function getDataViewId(dataViewsService: DataViewsService, patternName: string) { + const iph = new DataViewHandler(dataViewsService); + return await iph.getDataViewId(patternName); +} + +async function deleteDestDataViewById(dataViewsService: DataViewsService, dataViewId: string) { + const iph = new DataViewHandler(dataViewsService); + return await iph.deleteDataViewById(dataViewId); +} + +interface DeleteDataViewFnOptions { + dataViewsService: DataViewsService; + dataViewName: string; +} + +export const deleteDataViewFn = async ({ + dataViewsService, + dataViewName, +}: DeleteDataViewFnOptions): Promise => { + const response: DeleteDataViewApiResponseSchema = { + success: false, + }; + + try { + const dataViewId = await getDataViewId(dataViewsService, dataViewName); + if (dataViewId) { + await deleteDestDataViewById(dataViewsService, dataViewId); + } + response.success = true; + } catch (deleteDestDataViewError) { + response.error = deleteDestDataViewError; + } + + return response; +}; diff --git a/x-pack/packages/ml/data_view_utils/components/create_data_view_form_row.tsx b/x-pack/packages/ml/data_view_utils/components/create_data_view_form_row.tsx new file mode 100644 index 0000000000000..b252fc028b52b --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/components/create_data_view_form_row.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type FC } from 'react'; + +import { EuiFormRow, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { CreateDataViewSwitch } from './create_data_view_switch'; +import { CreateDataViewTimeField } from './create_data_view_time_field'; + +interface CreateDataViewFormProps { + canCreateDataView: boolean; + createDataView: boolean; + dataViewTitleExists: boolean; + setCreateDataView: (d: boolean) => void; + dataViewAvailableTimeFields: string[]; + dataViewTimeField: string | undefined; + onTimeFieldChanged: (e: React.ChangeEvent) => void; +} + +export const CreateDataViewForm: FC = ({ + canCreateDataView, + createDataView, + dataViewTitleExists, + setCreateDataView, + dataViewAvailableTimeFields, + dataViewTimeField, + onTimeFieldChanged, +}) => ( + <> + + {i18n.translate('xpack.ml.dataViewUtils.dataViewPermissionWarning', { + defaultMessage: 'You need permission to create data views.', + })} + , + ] + : []), + ...(createDataView && dataViewTitleExists + ? [ + i18n.translate('xpack.ml.dataViewUtils.dataViewTitleError', { + defaultMessage: 'A data view with this title already exists.', + }), + ] + : []), + ]} + > + + + {createDataView && !dataViewTitleExists && dataViewAvailableTimeFields.length > 0 && ( + + )} + +); diff --git a/x-pack/packages/ml/data_view_utils/components/create_data_view_switch.tsx b/x-pack/packages/ml/data_view_utils/components/create_data_view_switch.tsx new file mode 100644 index 0000000000000..9cef002fa974e --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/components/create_data_view_switch.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type FC } from 'react'; + +import { EuiSwitch } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +interface CreateDataViewSwitchProps { + canCreateDataView: boolean; + createDataView: boolean; + setCreateDataView: (d: boolean) => void; +} + +export const CreateDataViewSwitch: FC = ({ + canCreateDataView, + createDataView, + setCreateDataView, +}) => ( + setCreateDataView(!createDataView)} + data-test-subj="mlCreateDataViewSwitch" + /> +); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx b/x-pack/packages/ml/data_view_utils/components/create_data_view_time_field.tsx similarity index 72% rename from x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx rename to x-pack/packages/ml/data_view_utils/components/create_data_view_time_field.tsx index 76ce803bdb415..c86056ffa69c1 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx +++ b/x-pack/packages/ml/data_view_utils/components/create_data_view_time_field.tsx @@ -11,23 +11,20 @@ import { EuiFormRow, EuiSelect } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -interface Props { +interface CreateDataViewTimeFieldProps { dataViewAvailableTimeFields: string[]; dataViewTimeField: string | undefined; onTimeFieldChanged: (e: React.ChangeEvent) => void; } -export const StepDetailsTimeField: FC = ({ +export const CreateDataViewTimeField: FC = ({ dataViewAvailableTimeFields, dataViewTimeField, onTimeFieldChanged, }) => { - const noTimeFieldLabel = i18n.translate( - 'xpack.transform.stepDetailsForm.noTimeFieldOptionLabel', - { - defaultMessage: "I don't want to use the time field option", - } - ); + const noTimeFieldLabel = i18n.translate('xpack.ml.dataViewUtils.noTimeFieldOptionLabel', { + defaultMessage: "I don't want to use the time field option", + }); const noTimeFieldOption = { text: noTimeFieldLabel, @@ -44,13 +41,13 @@ export const StepDetailsTimeField: FC = ({ } helpText={ } @@ -63,7 +60,7 @@ export const StepDetailsTimeField: FC = ({ ]} value={dataViewTimeField} onChange={onTimeFieldChanged} - data-test-subj="transformDataViewTimeFieldSelect" + data-test-subj="mlDataViewTimeFieldSelect" /> ); diff --git a/x-pack/packages/ml/data_view_utils/jest.config.js b/x-pack/packages/ml/data_view_utils/jest.config.js new file mode 100644 index 0000000000000..783a52cc9f53e --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/data_view_utils'], +}; diff --git a/x-pack/packages/ml/data_view_utils/kibana.jsonc b/x-pack/packages/ml/data_view_utils/kibana.jsonc new file mode 100644 index 0000000000000..41251d1f7cbc2 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-data-view-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/data_view_utils/package.json b/x-pack/packages/ml/data_view_utils/package.json new file mode 100644 index 0000000000000..c7bbac1734158 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-data-view-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/packages/ml/data_view_utils/schemas/api_create_query_schema.ts b/x-pack/packages/ml/data_view_utils/schemas/api_create_query_schema.ts new file mode 100644 index 0000000000000..b0884b1a65f54 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/schemas/api_create_query_schema.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, type TypeOf } from '@kbn/config-schema'; + +export const dataViewCreateQuerySchema = schema.object({ + createDataView: schema.boolean({ defaultValue: false }), + timeFieldName: schema.maybe(schema.string()), +}); + +export type DataViewCreateQuerySchema = TypeOf; diff --git a/x-pack/packages/ml/data_view_utils/tsconfig.json b/x-pack/packages/ml/data_view_utils/tsconfig.json new file mode 100644 index 0000000000000..957b027bc2f20 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/data-views-plugin", + "@kbn/config-schema", + "@kbn/ml-error-utils", + "@kbn/i18n", + "@kbn/i18n-react", + ] +} diff --git a/x-pack/packages/ml/data_view_utils/types/api_create_response_schema.ts b/x-pack/packages/ml/data_view_utils/types/api_create_response_schema.ts new file mode 100644 index 0000000000000..5b752cd89e573 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/types/api_create_response_schema.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface DataViewCreated { + id: string; +} + +interface DataViewError { + id: string; + error: any; +} + +export interface CreateDataViewApiResponseSchema { + dataViewsCreated: DataViewCreated[]; + dataViewsErrors: DataViewError[]; +} diff --git a/x-pack/packages/ml/data_view_utils/types/api_delete_response_schema.ts b/x-pack/packages/ml/data_view_utils/types/api_delete_response_schema.ts new file mode 100644 index 0000000000000..2d1ee4ee0abd7 --- /dev/null +++ b/x-pack/packages/ml/data_view_utils/types/api_delete_response_schema.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Boom from '@hapi/boom'; + +import { EsErrorBody } from '@kbn/ml-error-utils'; + +/** + * Interface for data view API response for deletion status + */ +export interface DeleteDataViewApiResponseSchema { + /** + * Success + */ + success: boolean; + /** + * Optional error + */ + error?: EsErrorBody | Boom.Boom; +} diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 82258f4ef987e..917e7b5cac3e4 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -92,8 +92,14 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object } as const); export interface ModelDefinition { + /** + * Model name, e.g. elser + */ modelName: string; version: number; + /** + * Default PUT model configuration + */ config: object; description: string; os?: string; @@ -103,7 +109,10 @@ export interface ModelDefinition { hidden?: boolean; } -export type ModelDefinitionResponse = Omit & { +export type ModelDefinitionResponse = ModelDefinition & { + /** + * Complete model id, e.g. .elser_model_2_linux-x86_64 + */ name: string; }; diff --git a/x-pack/packages/ml/ui_actions/index.ts b/x-pack/packages/ml/ui_actions/index.ts index 8077406b58675..7a82ca6d68d9c 100644 --- a/x-pack/packages/ml/ui_actions/index.ts +++ b/x-pack/packages/ml/ui_actions/index.ts @@ -9,4 +9,11 @@ export { CREATE_PATTERN_ANALYSIS_TO_ML_AD_JOB_ACTION, CREATE_PATTERN_ANALYSIS_TO_ML_AD_JOB_TRIGGER, type CreateCategorizationADJobContext, -} from './src/ui_actions'; +} from './src/ml/ui_actions'; + +export { + ACTION_CATEGORIZE_FIELD, + CATEGORIZE_FIELD_TRIGGER, + categorizeFieldTrigger, + type CategorizeFieldContext, +} from './src/aiops/ui_actions'; diff --git a/x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts b/x-pack/packages/ml/ui_actions/src/aiops/ui_actions.ts new file mode 100644 index 0000000000000..b5e9ca07031ba --- /dev/null +++ b/x-pack/packages/ml/ui_actions/src/aiops/ui_actions.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 type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import type { Trigger } from '@kbn/ui-actions-plugin/public'; + +export interface CategorizeFieldContext { + field: DataViewField; + dataView: DataView; + originatingApp: string; + additionalFilter?: { + from: number; + to: number; + field?: { name: string; value: string }; + }; +} + +export const ACTION_CATEGORIZE_FIELD = 'ACTION_CATEGORIZE_FIELD'; + +export const CATEGORIZE_FIELD_TRIGGER = 'CATEGORIZE_FIELD_TRIGGER'; +export const categorizeFieldTrigger: Trigger = { + id: CATEGORIZE_FIELD_TRIGGER, + title: i18n.translate('xpack.ml.actions.runPatternAnalysis.title', { + defaultMessage: 'Run pattern analysis', + }), + description: i18n.translate('xpack.ml.actions.runPatternAnalysis.description', { + defaultMessage: 'Triggered when user wants to run pattern analysis on a field.', + }), +}; diff --git a/x-pack/packages/ml/ui_actions/src/ui_actions.ts b/x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts similarity index 100% rename from x-pack/packages/ml/ui_actions/src/ui_actions.ts rename to x-pack/packages/ml/ui_actions/src/ml/ui_actions.ts diff --git a/x-pack/packages/ml/ui_actions/tsconfig.json b/x-pack/packages/ml/ui_actions/tsconfig.json index 25ae95b59b163..ca3f66082b5c9 100644 --- a/x-pack/packages/ml/ui_actions/tsconfig.json +++ b/x-pack/packages/ml/ui_actions/tsconfig.json @@ -16,5 +16,7 @@ "kbn_references": [ "@kbn/data-views-plugin", "@kbn/es-query", + "@kbn/ui-actions-plugin", + "@kbn/i18n", ] } diff --git a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts index 9c7e1fb6acdb7..7ad732af36948 100644 --- a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts +++ b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -39,7 +38,6 @@ export const createCustomThresholdRule = async ( params: { criteria: ruleParams.params?.criteria || [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [1], timeSize: 1, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts index c2515cfe1615a..be2f2e344d7a5 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario1 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.LT, threshold: [100], timeSize: 1, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts index 68786ea4a06d2..fe8e2cfa01d32 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario2 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.LT, threshold: [40], timeSize: 1, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts index 97585a66b8385..bbb4d4e1c2551 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario3 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.LT, threshold: [5], timeSize: 1, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts index 0feec99ca5a47..dcf6048915ebf 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario4 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [80], timeSize: 1, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts index a36bb4af2544c..b42006a33a652 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario5 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [80], timeSize: 5, diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts index 16639c7749ca3..9bea0a00eb714 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -23,7 +22,6 @@ export const scenario6 = { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.LT, threshold: [1], timeSize: 1, diff --git a/x-pack/packages/security-solution/features/src/app_features_keys.ts b/x-pack/packages/security-solution/features/src/app_features_keys.ts index ab54c64cf8992..ed8923cdb229a 100644 --- a/x-pack/packages/security-solution/features/src/app_features_keys.ts +++ b/x-pack/packages/security-solution/features/src/app_features_keys.ts @@ -99,6 +99,7 @@ export enum SecuritySubFeatureId { /** Sub-features IDs for Cases */ export enum CasesSubFeatureId { deleteCases = 'deleteCasesSubFeature', + casesSettings = 'casesSettingsSubFeature', } /** Sub-features IDs for Security Assistant */ diff --git a/x-pack/packages/security-solution/features/src/cases/kibana_sub_features.ts b/x-pack/packages/security-solution/features/src/cases/kibana_sub_features.ts index 3cbdb3f0e9123..1f49d01f979da 100644 --- a/x-pack/packages/security-solution/features/src/cases/kibana_sub_features.ts +++ b/x-pack/packages/security-solution/features/src/cases/kibana_sub_features.ts @@ -17,6 +17,7 @@ import type { CasesFeatureParams } from './types'; */ export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [ CasesSubFeatureId.deleteCases, + CasesSubFeatureId.casesSettings, ]; /** @@ -60,7 +61,42 @@ export const getCasesSubFeaturesMap = ({ ], }; + const casesSettingsCasesSubFeature: SubFeatureConfig = { + name: i18n.translate( + 'securitySolutionPackages.features.featureRegistry.casesSettingsSubFeatureName', + { + defaultMessage: 'Case Settings', + } + ), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cases_settings', + name: i18n.translate( + 'securitySolutionPackages.features.featureRegistry.casesSettingsSubFeatureDetails', + { + defaultMessage: 'Edit Case Settings', + } + ), + includeIn: 'all', + savedObject: { + all: [...savedObjects.files], + read: [...savedObjects.files], + }, + cases: { + settings: [APP_ID], + }, + ui: uiCapabilities.settings, + }, + ], + }, + ], + }; + return new Map([ [CasesSubFeatureId.deleteCases, deleteCasesSubFeature], + [CasesSubFeatureId.casesSettings, casesSettingsCasesSubFeature], ]); }; diff --git a/x-pack/packages/security/plugin_types_common/README.md b/x-pack/packages/security/plugin_types_common/README.md new file mode 100644 index 0000000000000..eb7fbaa4a316f --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/README.md @@ -0,0 +1,4 @@ +# @kbn/security-plugin-types-common + +Contains type definitions for the Kibana Security plugin (common). + diff --git a/x-pack/packages/security/plugin_types_common/index.ts b/x-pack/packages/security/plugin_types_common/index.ts new file mode 100644 index 0000000000000..c5771f6982807 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + AuthenticatedUser, + UserRealm, + User, + AuthenticationProvider, +} from './src/authentication'; +export type { + Role, + RoleIndexPrivilege, + RoleKibanaPrivilege, + RoleRemoteIndexPrivilege, + FeaturesPrivileges, +} from './src/authorization'; +export type { SecurityLicense, SecurityLicenseFeatures, LoginLayout } from './src/licensing'; +export type { + UserProfileUserInfo, + UserProfileData, + UserProfileLabels, + UserProfile, + UserProfileWithSecurity, + UserProfileUserInfoWithSecurity, +} from './src/user_profile'; diff --git a/x-pack/packages/security/plugin_types_common/kibana.jsonc b/x-pack/packages/security/plugin_types_common/kibana.jsonc new file mode 100644 index 0000000000000..714eb0f564cda --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/security-plugin-types-common", + "owner": "@elastic/kibana-security" +} diff --git a/x-pack/packages/security/plugin_types_common/package.json b/x-pack/packages/security/plugin_types_common/package.json new file mode 100644 index 0000000000000..d28f1c03614d9 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/security-plugin-types-common", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts b/x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.ts new file mode 100644 index 0000000000000..ef58d67982977 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/authentication/authenticated_user.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AuthenticationProvider } from './authentication_provider'; +import type { User } from './user'; + +/** + * An Elasticsearch realm that was used to resolve and authenticate the user. + */ +export interface UserRealm { + /** + * Arbitrary name of the security realm. + */ + name: string; + + /** + * Type of the security realm (file, native, saml etc.). + */ + type: string; +} + +/** + * Represents the currently authenticated user. + */ +export interface AuthenticatedUser extends User { + /** + * The name and type of the Realm that has authenticated the user. + */ + authentication_realm: UserRealm; + + /** + * The name and type of the Realm where the user information were retrieved from. + */ + lookup_realm: UserRealm; + + /** + * The authentication provider that used to authenticate user. + */ + authentication_provider: AuthenticationProvider; + + /** + * The AuthenticationType used by ES to authenticate the user. + * + * @example "realm" | "api_key" | "token" | "anonymous" | "internal" + */ + authentication_type: string; + + /** + * Indicates whether user is authenticated via Elastic Cloud built-in SAML realm. + */ + elastic_cloud_user: boolean; + + /** + * User profile ID of this user. + */ + profile_uid?: string; +} diff --git a/x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts b/x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts new file mode 100644 index 0000000000000..92ec64d83db28 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/authentication/authentication_provider.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Type and name tuple to identify provider used to authenticate user. + */ +export interface AuthenticationProvider { + /** + * Type of the Kibana authentication provider. + */ + type: string; + /** + * Name of the Kibana authentication provider (arbitrary string). + */ + name: string; +} diff --git a/x-pack/packages/security/plugin_types_common/src/authentication/index.ts b/x-pack/packages/security/plugin_types_common/src/authentication/index.ts new file mode 100644 index 0000000000000..26b9873068c03 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/authentication/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { AuthenticatedUser, UserRealm } from './authenticated_user'; +export type { User } from './user'; +export type { AuthenticationProvider } from './authentication_provider'; diff --git a/x-pack/packages/security/plugin_types_common/src/authentication/user.ts b/x-pack/packages/security/plugin_types_common/src/authentication/user.ts new file mode 100644 index 0000000000000..17cd7cd100cab --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/authentication/user.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. + */ + +/** + * A set of fields describing Kibana user. + */ +export interface User { + username: string; + email?: string; + full_name?: string; + roles: readonly string[]; + enabled: boolean; + metadata?: { + _reserved: boolean; + _deprecated?: boolean; + _deprecated_reason?: string; + }; +} diff --git a/x-pack/plugins/security/common/model/features_privileges.ts b/x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts similarity index 100% rename from x-pack/plugins/security/common/model/features_privileges.ts rename to x-pack/packages/security/plugin_types_common/src/authorization/features_privileges.ts diff --git a/x-pack/test/lists_api_integration/common/ftr_provider_context.d.ts b/x-pack/packages/security/plugin_types_common/src/authorization/index.ts similarity index 59% rename from x-pack/test/lists_api_integration/common/ftr_provider_context.d.ts rename to x-pack/packages/security/plugin_types_common/src/authorization/index.ts index aa56557c09df8..29cb395ccfe4f 100644 --- a/x-pack/test/lists_api_integration/common/ftr_provider_context.d.ts +++ b/x-pack/packages/security/plugin_types_common/src/authorization/index.ts @@ -5,8 +5,10 @@ * 2.0. */ -import { GenericFtrProviderContext } from '@kbn/test'; - -import { services } from './services'; - -export type FtrProviderContext = GenericFtrProviderContext; +export type { FeaturesPrivileges } from './features_privileges'; +export type { + Role, + RoleKibanaPrivilege, + RoleIndexPrivilege, + RoleRemoteIndexPrivilege, +} from './role'; diff --git a/x-pack/packages/security/plugin_types_common/src/authorization/role.ts b/x-pack/packages/security/plugin_types_common/src/authorization/role.ts new file mode 100644 index 0000000000000..12fc0a85ff7aa --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/authorization/role.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FeaturesPrivileges } from './features_privileges'; + +export interface RoleIndexPrivilege { + names: string[]; + privileges: string[]; + field_security?: { + grant?: string[]; + except?: string[]; + }; + query?: string; +} + +export interface RoleRemoteIndexPrivilege extends RoleIndexPrivilege { + clusters: string[]; +} + +export interface RoleKibanaPrivilege { + spaces: string[]; + base: string[]; + feature: FeaturesPrivileges; + _reserved?: string[]; +} + +export interface Role { + name: string; + elasticsearch: { + cluster: string[]; + indices: RoleIndexPrivilege[]; + remote_indices?: RoleRemoteIndexPrivilege[]; + run_as: string[]; + }; + kibana: RoleKibanaPrivilege[]; + metadata?: { + [anyKey: string]: any; + }; + transient_metadata?: { + [anyKey: string]: any; + }; + _transform_error?: string[]; + _unrecognized_applications?: string[]; +} diff --git a/x-pack/packages/security/plugin_types_common/src/licensing/index.ts b/x-pack/packages/security/plugin_types_common/src/licensing/index.ts new file mode 100644 index 0000000000000..6fab9a2338fc8 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/licensing/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { SecurityLicense } from './license'; +export type { LoginLayout, SecurityLicenseFeatures } from './license_features'; diff --git a/x-pack/packages/security/plugin_types_common/src/licensing/license.ts b/x-pack/packages/security/plugin_types_common/src/licensing/license.ts new file mode 100644 index 0000000000000..041d76c0e8740 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/licensing/license.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; + +import type { LicenseType } from '@kbn/licensing-plugin/common/types'; + +import type { SecurityLicenseFeatures } from './license_features'; + +export interface SecurityLicense { + isLicenseAvailable(): boolean; + isEnabled(): boolean; + getFeatures(): SecurityLicenseFeatures; + hasAtLeast(licenseType: LicenseType): boolean | undefined; + features$: Observable; +} diff --git a/x-pack/plugins/security/common/licensing/license_features.ts b/x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts similarity index 100% rename from x-pack/plugins/security/common/licensing/license_features.ts rename to x-pack/packages/security/plugin_types_common/src/licensing/license_features.ts diff --git a/x-pack/packages/security/plugin_types_common/src/user_profile/index.ts b/x-pack/packages/security/plugin_types_common/src/user_profile/index.ts new file mode 100644 index 0000000000000..bffb7939c2fc8 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/user_profile/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + UserProfileUserInfo, + UserProfileData, + UserProfileLabels, + UserProfileUserInfoWithSecurity, + UserProfile, + UserProfileWithSecurity, +} from './user_profile'; diff --git a/x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts b/x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts new file mode 100644 index 0000000000000..13743974afea1 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/src/user_profile/user_profile.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * IMPORTANT: + * + * The types in this file are duplicated at + * `packages/kbn-user-profile-components/src/user_profile.ts` + * + * When making changes please ensure to keep both files in sync. + */ + +/** + * Describes basic properties stored in user profile. + */ +export interface UserProfile { + /** + * Unique ID for of the user profile. + */ + uid: string; + + /** + * Indicates whether user profile is enabled or not. + */ + enabled: boolean; + + /** + * Information about the user that owns profile. + */ + user: UserProfileUserInfo; + + /** + * User specific data associated with the profile. + */ + data: Partial; +} + +/** + * Basic user information returned in user profile. + */ +export interface UserProfileUserInfo { + /** + * Username of the user. + */ + username: string; + /** + * Optional email of the user. + */ + email?: string; + /** + * Optional full name of the user. + */ + full_name?: string; +} + +/** + * Placeholder for data stored in user profile. + */ +export type UserProfileData = Record; + +/** + * Type of the user profile labels structure (currently + */ +export type UserProfileLabels = Record; + +/** + * Extended user information returned in user profile (both basic and security related properties). + */ +export interface UserProfileUserInfoWithSecurity extends UserProfileUserInfo { + /** + * List of the user roles. + */ + roles: readonly string[]; + /** + * Name of the Elasticsearch security realm that was used to authenticate user. + */ + realm_name: string; + /** + * Optional name of the security domain that Elasticsearch security realm that was + * used to authenticate user resides in (if any). + */ + realm_domain?: string; +} + +/** + * Describes all properties stored in user profile (both basic and security related properties). + */ +export interface UserProfileWithSecurity< + D extends UserProfileData = UserProfileData, + L extends UserProfileLabels = UserProfileLabels +> extends UserProfile { + /** + * Information about the user that owns profile. + */ + user: UserProfileUserInfoWithSecurity; + + /** + * User specific _searchable_ labels associated with the profile. Note that labels are considered + * security related field since it's going to be used to store user's space ID. + */ + labels: L; +} diff --git a/x-pack/packages/security/plugin_types_common/tsconfig.json b/x-pack/packages/security/plugin_types_common/tsconfig.json new file mode 100644 index 0000000000000..74be218f1ec29 --- /dev/null +++ b/x-pack/packages/security/plugin_types_common/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/licensing-plugin" + ] +} diff --git a/x-pack/packages/security/plugin_types_public/README.md b/x-pack/packages/security/plugin_types_public/README.md new file mode 100644 index 0000000000000..fdc0481d60b68 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/README.md @@ -0,0 +1,4 @@ +# @kbn/security-plugin-types-public + +Contains type definitions for the Kibana Security plugin (public). + diff --git a/x-pack/packages/security/plugin_types_public/index.ts b/x-pack/packages/security/plugin_types_public/index.ts new file mode 100644 index 0000000000000..fc129909743e6 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { AuthenticationServiceStart, AuthenticationServiceSetup } from './src/authentication'; +export type { UserMenuLink, SecurityNavControlServiceStart } from './src/nav_control'; +export type { SecurityPluginSetup, SecurityPluginStart } from './src/plugin'; +export type { + GetUserProfileResponse, + UserProfileGetCurrentParams, + UserProfileBulkGetParams, + UserProfileSuggestParams, + UserProfileAPIClient, +} from './src/user_profile'; diff --git a/x-pack/packages/security/plugin_types_public/kibana.jsonc b/x-pack/packages/security/plugin_types_public/kibana.jsonc new file mode 100644 index 0000000000000..f4fbe8fe8ea7d --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-browser", + "id": "@kbn/security-plugin-types-public", + "owner": "@elastic/kibana-security" +} diff --git a/x-pack/packages/security/plugin_types_public/package.json b/x-pack/packages/security/plugin_types_public/package.json new file mode 100644 index 0000000000000..58e244244058c --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/security-plugin-types-public", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts b/x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts new file mode 100644 index 0000000000000..a8fa8a6a64d26 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/authentication/authentication_service.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; + +export interface AuthenticationServiceSetup { + /** + * Returns currently authenticated user and throws if current user isn't authenticated. + */ + getCurrentUser: () => Promise; + + /** + * Determines if API Keys are currently enabled. + */ + areAPIKeysEnabled: () => Promise; +} + +/** + * Start has the same contract as Setup for now. + */ +export type AuthenticationServiceStart = AuthenticationServiceSetup; diff --git a/x-pack/packages/security/plugin_types_public/src/authentication/index.ts b/x-pack/packages/security/plugin_types_public/src/authentication/index.ts new file mode 100644 index 0000000000000..55a977e551e22 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/authentication/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + AuthenticationServiceSetup, + AuthenticationServiceStart, +} from './authentication_service'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/index.ts b/x-pack/packages/security/plugin_types_public/src/nav_control/index.ts similarity index 72% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/index.ts rename to x-pack/packages/security/plugin_types_public/src/nav_control/index.ts index fbdb959e0e945..3250ac3c3ca79 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/index.ts +++ b/x-pack/packages/security/plugin_types_public/src/nav_control/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export { hostLensFormulas } from './host'; -export { kubernetesLensFormulas } from './kubernetes'; +export type { UserMenuLink, SecurityNavControlServiceStart } from './nav_control_service'; diff --git a/x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts b/x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts new file mode 100644 index 0000000000000..39982a753127c --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/nav_control/nav_control_service.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IconType } from '@elastic/eui'; +import type { ReactNode } from 'react'; +import type { Observable } from 'rxjs'; + +export interface UserMenuLink { + label: string; + iconType: IconType; + href: string; + order?: number; + setAsProfile?: boolean; + /** Render a custom ReactNode instead of the default */ + content?: ReactNode; +} + +export interface SecurityNavControlServiceStart { + /** + * Returns an Observable of the array of user menu links (the links that show up under the user's Avatar in the UI) registered by other plugins + */ + getUserMenuLinks$: () => Observable; + + /** + * Registers the provided user menu links to be displayed in the user menu (the links that show up under the user's Avatar in the UI). + */ + addUserMenuLinks: (newUserMenuLink: UserMenuLink[]) => void; +} diff --git a/x-pack/packages/security/plugin_types_public/src/plugin.ts b/x-pack/packages/security/plugin_types_public/src/plugin.ts new file mode 100644 index 0000000000000..28c5822a2e131 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/plugin.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SecurityLicense } from '@kbn/security-plugin-types-common'; +import type { AuthenticationServiceSetup, AuthenticationServiceStart } from './authentication'; +import type { SecurityNavControlServiceStart } from './nav_control'; +import type { UserProfileAPIClient } from './user_profile'; + +export interface SecurityPluginSetup { + /** + * Exposes authentication information about the currently logged in user. + */ + authc: AuthenticationServiceSetup; + /** + * Exposes information about the available security features under the current license. + */ + license: SecurityLicense; +} + +export interface SecurityPluginStart { + /** + * Exposes the ability to add custom links to the dropdown menu in the top right, where the user's Avatar is. + */ + navControlService: SecurityNavControlServiceStart; + /** + * Exposes authentication information about the currently logged in user. + */ + authc: AuthenticationServiceStart; + /** + * A set of methods to work with Kibana user profiles. + */ + userProfiles: UserProfileAPIClient; +} diff --git a/x-pack/packages/security/plugin_types_public/src/user_profile/index.ts b/x-pack/packages/security/plugin_types_public/src/user_profile/index.ts new file mode 100644 index 0000000000000..8840a7cee2723 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/user_profile/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + GetUserProfileResponse, + UserProfileAPIClient, + UserProfileGetCurrentParams, + UserProfileBulkGetParams, + UserProfileSuggestParams, +} from './user_profile_api_client'; diff --git a/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts b/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts new file mode 100644 index 0000000000000..090d5d5b0fcf4 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/user_profile/user_profile_api_client.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + UserProfileData, + AuthenticatedUser, + UserProfileWithSecurity, + UserProfile, +} from '@kbn/security-plugin-types-common'; +import type { Observable } from 'rxjs'; + +export interface UserProfileAPIClient { + readonly userProfile$: Observable; + /** + * Retrieves the user profile of the current user. If the profile isn't available, e.g. for the anonymous users or + * users authenticated via authenticating proxies, the `null` value is returned. + * @param [params] Get current user profile operation parameters. + * @param params.dataPath By default `getCurrent()` returns user information, but does not return any user data. The + * optional "dataPath" parameter can be used to return personal data for this user. + */ + getCurrent( + params?: UserProfileGetCurrentParams + ): Promise>; + + /** + * Retrieves multiple user profiles by their identifiers. + * @param params Bulk get operation parameters. + * @param params.uids List of user profile identifiers. + * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The + * optional "dataPath" parameter can be used to return personal data for the requested user profiles. + */ + bulkGet( + params: UserProfileBulkGetParams + ): Promise>>; + + /** + * Suggests multiple user profiles by search criteria. + * + * Note: This endpoint is not provided out-of-the-box by the platform. You need to expose your own + * version within your app. An example of how to do this can be found in: + * `examples/user_profile_examples/server/plugin.ts` + * + * @param path Path to your app's suggest endpoint. + * @param params Suggest operation parameters. + * @param params.name Query string used to match name-related fields in user profiles. The + * following fields are treated as name-related: username, full_name and email. + * @param params.size Desired number of suggestions to return. The default value is 10. + * @param params.dataPath By default, suggest API returns user information, but does not return + * any user data. The optional "dataPath" parameter can be used to return personal data for this + * user (within `kibana` namespace only). + */ + suggest( + path: string, + params: UserProfileSuggestParams + ): Promise>>; + + /** + * Updates user profile data of the current user. + * @param data Application data to be written (merged with existing data). + */ + update(data: D): Promise; +} + +/** + * Parameters for the get user profile for the current user API. + */ +export interface UserProfileGetCurrentParams { + /** + * By default, get API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath: string; +} + +export interface GetUserProfileResponse + extends UserProfileWithSecurity { + /** + * Information about the currently authenticated user that owns the profile. + */ + user: UserProfileWithSecurity['user'] & Pick; +} + +/** + * Parameters for the bulk get API. + */ +export interface UserProfileBulkGetParams { + /** + * List of user profile identifiers. + */ + uids: Set; + + /** + * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; +} + +/** + * Parameters for the suggest API. + */ +export interface UserProfileSuggestParams { + /** + * Query string used to match name-related fields in user profiles. The following fields are treated as + * name-related: username, full_name and email. + */ + name: string; + + /** + * Desired number of suggestions to return. The default value is 10. + */ + size?: number; + + /** + * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; +} diff --git a/x-pack/packages/security/plugin_types_public/tsconfig.json b/x-pack/packages/security/plugin_types_public/tsconfig.json new file mode 100644 index 0000000000000..23e34902d2c12 --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/security-plugin-types-common" + ] +} diff --git a/x-pack/packages/security/plugin_types_server/README.md b/x-pack/packages/security/plugin_types_server/README.md new file mode 100644 index 0000000000000..91ac1d3b2d187 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/README.md @@ -0,0 +1,4 @@ +# @kbn/security-plugin-types-server + +Contains type definitions for the Kibana Security plugin (server). + diff --git a/x-pack/packages/security/plugin_types_server/index.ts b/x-pack/packages/security/plugin_types_server/index.ts new file mode 100644 index 0000000000000..2d697dd0187ab --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + AuditEvent, + AuditHttp, + AuditKibana, + AuditRequest, + AuditServiceSetup, + AuditLogger, +} from './src/audit'; +export type { + CreateAPIKeyParams, + CreateAPIKeyResult, + CreateRestAPIKeyParams, + GrantAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + InvalidateAPIKeyResult, + APIKeys, + AuthenticationServiceStart, +} from './src/authentication'; +export type { + PrivilegeDeprecationsService, + PrivilegeDeprecationsRolesByFeatureIdResponse, + PrivilegeDeprecationsRolesByFeatureIdRequest, + CheckPrivilegesResponse, + CheckPrivilegesWithRequest, + CheckSavedObjectsPrivilegesWithRequest, + CheckPrivilegesDynamicallyWithRequest, + KibanaPrivilegesType, + SavedObjectActions, + UIActions, + CheckPrivilegesPayload, + CheckSavedObjectsPrivileges, + HasPrivilegesResponse, + HasPrivilegesResponseApplication, + SpaceActions, + Actions, + CheckPrivilegesOptions, + CheckUserProfilesPrivilegesPayload, + CheckUserProfilesPrivilegesResponse, + ElasticsearchPrivilegesType, + CasesActions, + CheckPrivileges, + AlertingActions, + AppActions, + ApiActions, + CheckPrivilegesDynamically, + CheckUserProfilesPrivileges, + AuthorizationMode, + AuthorizationServiceSetup, +} from './src/authorization'; +export type { SecurityPluginSetup, SecurityPluginStart } from './src/plugin'; +export type { + UserProfileServiceStart, + UserProfileSuggestParams, + UserProfileGetCurrentParams, + UserProfileBulkGetParams, + UserProfileRequiredPrivileges, +} from './src/user_profile'; + +export { + restApiKeySchema, + getRestApiKeyWithKibanaPrivilegesSchema, + crossClusterApiKeySchema, +} from './src/authentication'; +export { GLOBAL_RESOURCE, elasticsearchRoleSchema, getKibanaRoleSchema } from './src/authorization'; diff --git a/x-pack/packages/security/plugin_types_server/kibana.jsonc b/x-pack/packages/security/plugin_types_server/kibana.jsonc new file mode 100644 index 0000000000000..e4f4a074f6e76 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-server", + "id": "@kbn/security-plugin-types-server", + "owner": "@elastic/kibana-security" +} diff --git a/x-pack/packages/security/plugin_types_server/package.json b/x-pack/packages/security/plugin_types_server/package.json new file mode 100644 index 0000000000000..da6a8b5eb0b00 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/security-plugin-types-server", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts b/x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts new file mode 100644 index 0000000000000..f071a3edcd824 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/audit/audit_events.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LogMeta } from '@kbn/core/server'; + +/** + * Audit kibana schema using ECS format + */ +export interface AuditKibana { + /** + * The ID of the space associated with this event. + */ + space_id?: string; + /** + * The ID of the user session associated with this event. Each login attempt + * results in a unique session id. + */ + session_id?: string; + /** + * Saved object that was created, changed, deleted or accessed as part of this event. + */ + saved_object?: { + type: string; + id: string; + }; + /** + * Name of authentication provider associated with a login event. + */ + authentication_provider?: string; + /** + * Type of authentication provider associated with a login event. + */ + authentication_type?: string; + /** + * Name of Elasticsearch realm that has authenticated the user. + */ + authentication_realm?: string; + /** + * Name of Elasticsearch realm where the user details were retrieved from. + */ + lookup_realm?: string; + /** + * Set of space IDs that a saved object was shared to. + */ + add_to_spaces?: readonly string[]; + /** + * Set of space IDs that a saved object was removed from. + */ + delete_from_spaces?: readonly string[]; + /** + * Set of space IDs that are not authorized for an action. + */ + unauthorized_spaces?: readonly string[]; + /** + * Set of types that are not authorized for an action. + */ + unauthorized_types?: readonly string[]; +} + +type EcsHttp = Required['http']; +type EcsRequest = Required['request']; + +/** + * Audit request schema using ECS format + */ +export interface AuditRequest extends EcsRequest { + /** + * HTTP request headers + */ + headers?: { + 'x-forwarded-for'?: string; + }; +} + +/** + * Audit http schema using ECS format + */ +export interface AuditHttp extends EcsHttp { + /** + * HTTP request details + */ + request?: AuditRequest; +} + +/** + * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html + * + * If you add additional fields to the schema ensure you update the Kibana Filebeat module: + * https://github.com/elastic/beats/tree/master/filebeat/module/kibana + * + * @public + */ +export interface AuditEvent extends LogMeta { + /** + * Log message + */ + message: string; + + /** + * Kibana specific fields + */ + kibana?: AuditKibana; + + /** + * Fields describing an HTTP request + */ + http?: AuditHttp; +} diff --git a/x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts b/x-pack/packages/security/plugin_types_server/src/audit/audit_logger.ts new file mode 100644 index 0000000000000..4670de3aa8d3b --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/audit/audit_logger.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 { AuditEvent } from './audit_events'; + +export interface AuditLogger { + /** + * Logs an {@link AuditEvent} and automatically adds meta data about the + * current user, space and correlation id. + * + * Guidelines around what events should be logged and how they should be + * structured can be found in: `/x-pack/plugins/security/README.md` + * + * @example + * ```typescript + * const auditLogger = securitySetup.audit.asScoped(request); + * auditLogger.log({ + * message: 'User is updating dashboard [id=123]', + * event: { + * action: 'saved_object_update', + * outcome: 'unknown' + * }, + * kibana: { + * saved_object: { type: 'dashboard', id: '123' } + * }, + * }); + * ``` + */ + log: (event: AuditEvent | undefined) => void; + + /** + * Indicates whether audit logging is enabled or not. + * + * Useful for skipping resource-intense operations that don't need to be performed when audit + * logging is disabled. + */ + readonly enabled: boolean; +} diff --git a/x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts b/x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts new file mode 100644 index 0000000000000..88b25b5181a42 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/audit/audit_service.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core/server'; + +import type { AuditLogger } from './audit_logger'; + +export interface AuditServiceSetup { + /** + * Creates an {@link AuditLogger} scoped to the current request. + * + * This audit logger logs events with all required user and session info and should be used for + * all user-initiated actions. + * + * @example + * ```typescript + * const auditLogger = securitySetup.audit.asScoped(request); + * auditLogger.log(event); + * ``` + */ + asScoped: (request: KibanaRequest) => AuditLogger; + + /** + * {@link AuditLogger} for background tasks only. + * + * This audit logger logs events without any user or session info and should never be used to log + * user-initiated actions. + * + * @example + * ```typescript + * securitySetup.audit.withoutRequest.log(event); + * ``` + */ + withoutRequest: AuditLogger; +} diff --git a/x-pack/packages/security/plugin_types_server/src/audit/index.ts b/x-pack/packages/security/plugin_types_server/src/audit/index.ts new file mode 100644 index 0000000000000..0111172cd409f --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/audit/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { AuditServiceSetup } from './audit_service'; +export type { AuditEvent, AuditHttp, AuditKibana, AuditRequest } from './audit_events'; +export type { AuditLogger } from './audit_logger'; diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts new file mode 100644 index 0000000000000..1cbf13a4ad45f --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts @@ -0,0 +1,201 @@ +/* + * Copyright 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 { estypes } from '@elastic/elasticsearch'; + +import type { KibanaRequest } from '@kbn/core/server'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { getKibanaRoleSchema, elasticsearchRoleSchema } from '../../authorization'; + +export interface APIKeys { + /** + * Determines if API Keys are enabled in Elasticsearch. + */ + areAPIKeysEnabled(): Promise; + + /** + * Determines if Cross-Cluster API Keys are enabled in Elasticsearch. + */ + areCrossClusterAPIKeysEnabled(): Promise; + + /** + * Tries to create an API key for the current user. + * + * Returns newly created API key or `null` if API keys are disabled. + * + * User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys. + * + * @param request Request instance. + * @param createParams The params to create an API key + */ + create( + request: KibanaRequest, + createParams: CreateAPIKeyParams + ): Promise; + + /** + * Tries to grant an API key for the current user. + * @param request Request instance. + * @param createParams Create operation parameters. + */ + grantAsInternalUser( + request: KibanaRequest, + createParams: CreateRestAPIKeyParams | CreateRestAPIKeyWithKibanaPrivilegesParams + ): Promise; + + /** + * Tries to validate an API key. + * @param apiKeyPrams ValidateAPIKeyParams. + */ + validate(apiKeyPrams: ValidateAPIKeyParams): Promise; + + /** + * Tries to invalidate an API keys. + * @param request Request instance. + * @param params The params to invalidate an API keys. + */ + invalidate( + request: KibanaRequest, + params: InvalidateAPIKeysParams + ): Promise; + + /** + * Tries to invalidate the API keys by using the internal user. + * @param params The params to invalidate the API keys. + */ + invalidateAsInternalUser(params: InvalidateAPIKeysParams): Promise; +} + +export type CreateAPIKeyParams = + | CreateRestAPIKeyParams + | CreateRestAPIKeyWithKibanaPrivilegesParams + | CreateCrossClusterAPIKeyParams; + +/** + * Response of Kibana Create API key endpoint. + */ +export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse; + +export type CreateRestAPIKeyParams = TypeOf; +export type CreateRestAPIKeyWithKibanaPrivilegesParams = TypeOf< + ReturnType +>; +export type CreateCrossClusterAPIKeyParams = TypeOf; + +export interface GrantAPIKeyResult { + /** + * Unique id for this API key + */ + id: string; + /** + * Name for this API key + */ + name: string; + /** + * Generated API key + */ + api_key: string; +} + +/** + * Represents the parameters for validating API Key credentials. + */ +export interface ValidateAPIKeyParams { + /** + * Unique id for this API key + */ + id: string; + + /** + * Generated API Key (secret) + */ + api_key: string; +} + +/** + * Represents the params for invalidating multiple API keys + */ +export interface InvalidateAPIKeysParams { + ids: string[]; +} + +/** + * The return value when invalidating an API key in Elasticsearch. + */ +export interface InvalidateAPIKeyResult { + /** + * The IDs of the API keys that were invalidated as part of the request. + */ + invalidated_api_keys: string[]; + /** + * The IDs of the API keys that were already invalidated. + */ + previously_invalidated_api_keys: string[]; + /** + * The number of errors that were encountered when invalidating the API keys. + */ + error_count: number; + /** + * Details about these errors. This field is not present in the response when error_count is 0. + */ + error_details?: Array<{ + type?: string; + reason?: string; + caused_by?: { + type?: string; + reason?: string; + }; + }>; +} + +export const restApiKeySchema = schema.object({ + type: schema.maybe(schema.literal('rest')), + name: schema.string(), + expiration: schema.maybe(schema.string()), + role_descriptors: schema.recordOf(schema.string(), schema.object({}, { unknowns: 'allow' }), { + defaultValue: {}, + }), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), +}); + +export const getRestApiKeyWithKibanaPrivilegesSchema = ( + getBasePrivilegeNames: Parameters[0] +) => + restApiKeySchema.extends({ + role_descriptors: null, + kibana_role_descriptors: schema.recordOf( + schema.string(), + schema.object({ + elasticsearch: elasticsearchRoleSchema.extends({}, { unknowns: 'allow' }), + kibana: getKibanaRoleSchema(getBasePrivilegeNames), + }) + ), + }); + +export const crossClusterApiKeySchema = restApiKeySchema.extends({ + type: schema.literal('cross_cluster'), + role_descriptors: null, + access: schema.object( + { + search: schema.maybe( + schema.arrayOf( + schema.object({ + names: schema.arrayOf(schema.string()), + }) + ) + ), + replication: schema.maybe( + schema.arrayOf( + schema.object({ + names: schema.arrayOf(schema.string()), + }) + ) + ), + }, + { unknowns: 'allow' } + ), +}); diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts new file mode 100644 index 0000000000000..dbad1344d1d24 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + CreateAPIKeyParams, + CreateAPIKeyResult, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + CreateRestAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + GrantAPIKeyResult, + APIKeys, +} from './api_keys'; +export { + crossClusterApiKeySchema, + getRestApiKeyWithKibanaPrivilegesSchema, + restApiKeySchema, +} from './api_keys'; diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts b/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts new file mode 100644 index 0000000000000..5dc8827786a4b --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core/server'; +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; + +import type { APIKeys } from './api_keys'; + +/** + * Authentication services available on the security plugin's start contract. + */ +export interface AuthenticationServiceStart { + apiKeys: APIKeys; + getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/index.ts b/x-pack/packages/security/plugin_types_server/src/authentication/index.ts new file mode 100644 index 0000000000000..04e4a820fb4d9 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authentication/index.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. + */ + +export type { + CreateAPIKeyParams, + CreateAPIKeyResult, + CreateRestAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + APIKeys, + GrantAPIKeyResult, +} from './api_keys'; +export type { AuthenticationServiceStart } from './authentication_service'; +export { + restApiKeySchema, + getRestApiKeyWithKibanaPrivilegesSchema, + crossClusterApiKeySchema, +} from './api_keys'; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts new file mode 100644 index 0000000000000..e1ad16afa5d30 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/actions.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertingActions } from './alerting'; +import type { ApiActions } from './api'; +import type { AppActions } from './app'; +import type { CasesActions } from './cases'; +import type { SavedObjectActions } from './saved_object'; +import type { SpaceActions } from './space'; +import type { UIActions } from './ui'; + +/** Actions are used to create the "actions" that are associated with Elasticsearch's + * application privileges, and are used to perform the authorization checks implemented + * by the various `checkPrivilegesWithRequest` derivatives. + */ +export interface Actions { + readonly api: ApiActions; + readonly app: AppActions; + readonly cases: CasesActions; + readonly login: string; + readonly savedObject: SavedObjectActions; + readonly alerting: AlertingActions; + readonly space: SpaceActions; + readonly ui: UIActions; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts new file mode 100644 index 0000000000000..fe27a15d1ec07 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/alerting.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface AlertingActions { + get(ruleTypeId: string, consumer: string, alertingEntity: string, operation: string): string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts new file mode 100644 index 0000000000000..30a1328ce5639 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/api.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface ApiActions { + get(operation: string): string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts new file mode 100644 index 0000000000000..38125e45bdfe6 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface AppActions { + get(operation: string): string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts new file mode 100644 index 0000000000000..974c106f14d1b --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/cases.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface CasesActions { + get(owner: string, operation: string): string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/index.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/index.ts new file mode 100644 index 0000000000000..6b3993423015f --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { Actions } from './actions'; +export type { AlertingActions } from './alerting'; +export type { ApiActions } from './api'; +export type { AppActions } from './app'; +export type { CasesActions } from './cases'; +export type { SavedObjectActions } from './saved_object'; +export type { SpaceActions } from './space'; +export type { UIActions } from './ui'; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts new file mode 100644 index 0000000000000..329feb5967043 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/saved_object.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface SavedObjectActions { + get(type: string, operation: string): string; +} diff --git a/x-pack/test/lists_api_integration/common/services.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts similarity index 81% rename from x-pack/test/lists_api_integration/common/services.ts rename to x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts index 7e415338c405f..fb23efaa084b9 100644 --- a/x-pack/test/lists_api_integration/common/services.ts +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/space.ts @@ -5,4 +5,6 @@ * 2.0. */ -export { services } from '../../api_integration/services'; +export interface SpaceActions { + manage: string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts b/x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts new file mode 100644 index 0000000000000..176248553898f --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Capabilities as UICapabilities } from '@kbn/core/server'; + +export interface UIActions { + get(featureId: keyof UICapabilities, ...uiCapabilityParts: string[]): string; +} diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts b/x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts new file mode 100644 index 0000000000000..82fa3d3fcce5e --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/authorization_service.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Actions } from './actions'; +import type { CheckPrivilegesWithRequest } from './check_privileges'; +import type { CheckPrivilegesDynamicallyWithRequest } from './check_privileges_dynamically'; +import type { CheckSavedObjectsPrivilegesWithRequest } from './check_saved_objects_privileges'; +import type { AuthorizationMode } from './mode'; + +/** + * Authorization services available on the setup contract of the security plugin. + */ +export interface AuthorizationServiceSetup { + /** + * Actions are used to create the "actions" that are associated with Elasticsearch's + * application privileges, and are used to perform the authorization checks implemented + * by the various `checkPrivilegesWithRequest` derivatives. + */ + actions: Actions; + checkPrivilegesWithRequest: CheckPrivilegesWithRequest; + checkPrivilegesDynamicallyWithRequest: CheckPrivilegesDynamicallyWithRequest; + checkSavedObjectsPrivilegesWithRequest: CheckSavedObjectsPrivilegesWithRequest; + mode: AuthorizationMode; +} diff --git a/x-pack/plugins/security/server/authorization/types.ts b/x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts similarity index 100% rename from x-pack/plugins/security/server/authorization/types.ts rename to x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts b/x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.ts new file mode 100644 index 0000000000000..f9663dddc64d0 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/check_privileges_dynamically.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 type { KibanaRequest } from '@kbn/core/server'; +import type { + CheckPrivilegesPayload, + CheckPrivilegesOptions, + CheckPrivilegesResponse, +} from './check_privileges'; + +export type CheckPrivilegesDynamically = ( + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions +) => Promise; + +export type CheckPrivilegesDynamicallyWithRequest = ( + request: KibanaRequest +) => CheckPrivilegesDynamically; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts b/x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.ts new file mode 100644 index 0000000000000..4b42723c83286 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/check_saved_objects_privileges.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 type { KibanaRequest } from '@kbn/core/server'; +import type { CheckPrivilegesResponse } from './check_privileges'; + +export type CheckSavedObjectsPrivilegesWithRequest = ( + request: KibanaRequest +) => CheckSavedObjectsPrivileges; + +export type CheckSavedObjectsPrivileges = ( + actions: string | string[], + namespaceOrNamespaces?: string | Array +) => Promise; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/constants.ts b/x-pack/packages/security/plugin_types_server/src/authorization/constants.ts new file mode 100644 index 0000000000000..cd2d7d3002558 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const GLOBAL_RESOURCE = '*'; diff --git a/x-pack/plugins/security/common/model/deprecations.ts b/x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts similarity index 92% rename from x-pack/plugins/security/common/model/deprecations.ts rename to x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts index 3fa9bd4019818..68cc61067e3c0 100644 --- a/x-pack/plugins/security/common/model/deprecations.ts +++ b/x-pack/packages/security/plugin_types_server/src/authorization/deprecations.ts @@ -7,7 +7,7 @@ import type { DeprecationsDetails, GetDeprecationsContext } from '@kbn/core/server'; -import type { Role } from './role'; +import type { Role } from '@kbn/security-plugin-types-common'; export interface PrivilegeDeprecationsRolesByFeatureIdResponse { roles?: Role[]; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/index.ts b/x-pack/packages/security/plugin_types_server/src/authorization/index.ts new file mode 100644 index 0000000000000..54364d7817f31 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + Actions, + ApiActions, + AppActions, + AlertingActions, + CasesActions, + SavedObjectActions, + SpaceActions, + UIActions, +} from './actions'; +export type { AuthorizationServiceSetup } from './authorization_service'; +export type { + CheckPrivilegesOptions, + CheckPrivilegesResponse, + CheckPrivilegesWithRequest, + CheckPrivilegesPayload, + CheckPrivileges, + HasPrivilegesResponse, + HasPrivilegesResponseApplication, + CheckUserProfilesPrivilegesPayload, + CheckUserProfilesPrivilegesResponse, + CheckUserProfilesPrivileges, +} from './check_privileges'; +export type { + CheckPrivilegesDynamically, + CheckPrivilegesDynamicallyWithRequest, +} from './check_privileges_dynamically'; +export type { + CheckSavedObjectsPrivileges, + CheckSavedObjectsPrivilegesWithRequest, +} from './check_saved_objects_privileges'; +export type { + PrivilegeDeprecationsService, + PrivilegeDeprecationsRolesByFeatureIdRequest, + PrivilegeDeprecationsRolesByFeatureIdResponse, +} from './deprecations'; +export type { AuthorizationMode } from './mode'; +export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './role_schema'; + +export { GLOBAL_RESOURCE } from './constants'; +export { elasticsearchRoleSchema, getKibanaRoleSchema } from './role_schema'; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/mode.ts b/x-pack/packages/security/plugin_types_server/src/authorization/mode.ts new file mode 100644 index 0000000000000..242b5524a937a --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/authorization/mode.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core/server'; + +export interface AuthorizationMode { + useRbacForRequest(request: KibanaRequest): boolean; +} diff --git a/x-pack/plugins/security/server/lib/role_schema.ts b/x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts similarity index 99% rename from x-pack/plugins/security/server/lib/role_schema.ts rename to x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts index fe59a1f740b41..ea7f896927905 100644 --- a/x-pack/plugins/security/server/lib/role_schema.ts +++ b/x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { GLOBAL_RESOURCE } from '../../common/constants'; +import { GLOBAL_RESOURCE } from './constants'; /** * Elasticsearch specific portion of the role definition. diff --git a/x-pack/packages/security/plugin_types_server/src/plugin.ts b/x-pack/packages/security/plugin_types_server/src/plugin.ts new file mode 100644 index 0000000000000..d3ee046c2d0cd --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/plugin.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SecurityLicense } from '@kbn/security-plugin-types-common'; +import type { AuditServiceSetup } from './audit'; +import type { PrivilegeDeprecationsService, AuthorizationServiceSetup } from './authorization'; +import type { AuthenticationServiceStart } from './authentication'; +import type { UserProfileServiceStart } from './user_profile'; + +/** + * Describes public Security plugin contract returned at the `setup` stage. + */ +export interface SecurityPluginSetup { + /** + * Exposes information about the available security features under the current license. + */ + license: SecurityLicense; + /** + * Exposes services for audit logging. + */ + audit: AuditServiceSetup; + /** + * Exposes services to access kibana roles per feature id with the GetDeprecationsContext + */ + privilegeDeprecationsService: PrivilegeDeprecationsService; +} + +/** + * Describes public Security plugin contract returned at the `start` stage. + */ +export interface SecurityPluginStart { + /** + * Authentication services to confirm the user is who they say they are. + */ + authc: AuthenticationServiceStart; + /** + * Authorization services to manage and access the permissions a particular user has. + */ + authz: AuthorizationServiceSetup; + /** + * User profiles services to retrieve user profiles. + */ + userProfiles: UserProfileServiceStart; +} diff --git a/x-pack/packages/security/plugin_types_server/src/user_profile/index.ts b/x-pack/packages/security/plugin_types_server/src/user_profile/index.ts new file mode 100644 index 0000000000000..15be67e7601f0 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/user_profile/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + UserProfileServiceStart, + UserProfileSuggestParams, + UserProfileBulkGetParams, + UserProfileRequiredPrivileges, + UserProfileGetCurrentParams, +} from './user_profile_service'; diff --git a/x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts b/x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts new file mode 100644 index 0000000000000..ab68b973139ed --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/src/user_profile/user_profile_service.ts @@ -0,0 +1,142 @@ +/* + * Copyright 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 { KibanaRequest } from '@kbn/core/server'; +import type { + UserProfileData, + UserProfileLabels, + UserProfileWithSecurity, + UserProfile, +} from '@kbn/security-plugin-types-common'; + +/** + * A set of methods to work with Kibana user profiles. + */ +export interface UserProfileServiceStart { + /** + * Retrieves a user profile for the current user extracted from the specified request. If the profile isn't available, + * e.g. for the anonymous users or users authenticated via authenticating proxies, the `null` value is returned. + * @param params Get current user profile operation parameters. + * @param params.request User request instance to get user profile for. + * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The + * optional "dataPath" parameter can be used to return personal data for the requested user profiles. + */ + getCurrent( + params: UserProfileGetCurrentParams + ): Promise | null>; + + /** + * Retrieves multiple user profiles by their identifiers. + * @param params Bulk get operation parameters. + * @param params.uids List of user profile identifiers. + * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The + * optional "dataPath" parameter can be used to return personal data for the requested user profiles. + */ + bulkGet( + params: UserProfileBulkGetParams + ): Promise>>; + + /** + * Suggests multiple user profiles by search criteria. + * @param params Suggest operation parameters. + * @param params.name Query string used to match name-related fields in user profiles. The following fields are treated as name-related: username, full_name and email. + * @param params.size Desired number of suggestion to return. The default value is 10. + * @param params.dataPath By default, suggest API returns user information, but does not return any user data. The optional "dataPath" parameter can be used to return personal data for this user (within `kibana` namespace only). + * @param params.requiredPrivileges The set of the privileges that users associated with the suggested user profile should have in the specified space. If not specified, privileges check isn't performed and all matched profiles are returned irrespective to the privileges of the associated users. + */ + suggest( + params: UserProfileSuggestParams + ): Promise>>; +} + +/** + * The set of privileges that users associated with the suggested user profile should have for a specified space id. + */ +export interface UserProfileRequiredPrivileges { + /** + * The id of the Kibana Space. + */ + spaceId: string; + + /** + * The set of the Kibana specific application privileges. + */ + privileges: { kibana: string[] }; +} + +/** + * Parameters for the get user profile for the current user API. + */ +export interface UserProfileGetCurrentParams { + /** + * User request instance to get user profile for. + */ + request: KibanaRequest; + + /** + * By default, get API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; +} + +/** + * Parameters for the bulk get API. + */ +export interface UserProfileBulkGetParams { + /** + * List of user profile identifiers. + */ + uids: Set; + + /** + * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; +} + +/** + * Parameters for the suggest API. + */ +export interface UserProfileSuggestParams { + /** + * Query string used to match name-related fields in user profiles. The following fields are treated as + * name-related: username, full_name and email. + */ + name?: string; + + /** + * Extra search criteria to improve relevance of the suggestion result. A profile matching the + * specified hint is ranked higher in the response. But not-matching the hint does not exclude a + * profile from the response as long as it matches the `name` field query. + */ + hint?: { + /** + * A list of Profile UIDs to match against. + */ + uids: string[]; + }; + + /** + * Desired number of suggestion to return. The default value is 10. + */ + size?: number; + + /** + * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" + * parameter can be used to return personal data for this user (within `kibana` namespace only). + */ + dataPath?: string; + + /** + * The set of the privileges that users associated with the suggested user profile should have in the specified space. + * If not specified, privileges check isn't performed and all matched profiles are returned irrespective to the + * privileges of the associated users. + */ + requiredPrivileges?: UserProfileRequiredPrivileges; +} diff --git a/x-pack/packages/security/plugin_types_server/tsconfig.json b/x-pack/packages/security/plugin_types_server/tsconfig.json new file mode 100644 index 0000000000000..1883d50f328e5 --- /dev/null +++ b/x-pack/packages/security/plugin_types_server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/config-schema", + "@kbn/core", + "@kbn/security-plugin-types-common", + ] +} diff --git a/x-pack/plugins/actions/docs/openapi/README.md b/x-pack/plugins/actions/docs/openapi/README.md index 2bce08f0b1a4c..210bb719eb918 100644 --- a/x-pack/plugins/actions/docs/openapi/README.md +++ b/x-pack/plugins/actions/docs/openapi/README.md @@ -1,6 +1,6 @@ # OpenAPI (Experimental) -The current self-contained spec file is [as JSON](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.json) or [as YAML](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.yaml) and can be used for online tools like those found at https://openapi.tools/. +The current self-contained spec file is [as JSON](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.json) or [as YAML](https://raw.githubusercontent.com/elastic/kibana/master/x-pack/plugins/cases/common/openapi/bundled.yaml) and can be used for online tools like those found at https://openapi.tools/. 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/). diff --git a/x-pack/plugins/actions/docs/openapi/bundled.json b/x-pack/plugins/actions/docs/openapi/bundled.json index 2f8bbf898efee..a2debc66a17a7 100644 --- a/x-pack/plugins/actions/docs/openapi/bundled.json +++ b/x-pack/plugins/actions/docs/openapi/bundled.json @@ -1,9 +1,9 @@ { - "openapi": "3.0.1", + "openapi": "3.1.0", "info": { "title": "Connectors", "description": "OpenAPI schema for Connectors endpoints", - "version": "0.1", + "version": "0.2", "contact": { "name": "Connectors Team" }, @@ -178,7 +178,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } } ], @@ -303,6 +305,9 @@ "runJiraConnectorRequest": { "$ref": "#/components/examples/run_jira_connector_request" }, + "runPagerDutyConnectorRequest": { + "$ref": "#/components/examples/run_pagerduty_connector_request" + }, "runServerLogConnectorRequest": { "$ref": "#/components/examples/run_server_log_connector_request" }, @@ -368,6 +373,9 @@ "runJiraConnectorResponse": { "$ref": "#/components/examples/run_jira_connector_response" }, + "runPagerDutyConnectorResponse": { + "$ref": "#/components/examples/run_pagerduty_connector_response" + }, "runServerLogConnectorResponse": { "$ref": "#/components/examples/run_server_log_connector_response" }, @@ -451,34 +459,46 @@ "enabled": { "type": "boolean", "description": "Indicates whether the connector type is enabled in Kibana.", - "example": true + "examples": [ + true + ] }, "enabled_in_config": { "type": "boolean", "description": "Indicates whether the connector type is enabled in the Kibana configuration file.", - "example": true + "examples": [ + true + ] }, "enabled_in_license": { "type": "boolean", "description": "Indicates whether the connector is enabled in the license.", - "example": true + "examples": [ + true + ] }, "id": { "$ref": "#/components/schemas/connector_types" }, "is_system_action_type": { "type": "boolean", - "example": false + "examples": [ + false + ] }, "minimum_license_required": { "type": "string", "description": "The license that is required to use the connector type.", - "example": "basic" + "examples": [ + "basic" + ] }, "name": { "type": "string", "description": "The name of the connector type.", - "example": "Index" + "examples": [ + "Index" + ] }, "supported_feature_ids": { "type": "array", @@ -486,10 +506,12 @@ "items": { "$ref": "#/components/schemas/features" }, - "example": [ - "alerting", - "cases", - "siem" + "examples": [ + [ + "alerting", + "cases", + "siem" + ] ] } } @@ -671,7 +693,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } } ], @@ -841,17 +865,23 @@ "enabled": { "type": "boolean", "description": "Indicates whether the connector type is enabled in Kibana.", - "example": true + "examples": [ + true + ] }, "enabled_in_config": { "type": "boolean", "description": "Indicates whether the connector type is enabled in the Kibana `.yml` file.", - "example": true + "examples": [ + true + ] }, "enabled_in_license": { "type": "boolean", "description": "Indicates whether the connector is enabled in the license.", - "example": true + "examples": [ + true + ] }, "id": { "$ref": "#/components/schemas/connector_types" @@ -859,12 +889,16 @@ "minimum_license_required": { "type": "string", "description": "The license that is required to use the connector type.", - "example": "basic" + "examples": [ + "basic" + ] }, "name": { "type": "string", "description": "The name of the connector type.", - "example": "Index" + "examples": [ + "Index" + ] }, "supported_feature_ids": { "type": "array", @@ -872,10 +906,12 @@ "items": { "$ref": "#/components/schemas/features" }, - "example": [ - "alerting", - "uptime", - "siem" + "examples": [ + [ + "alerting", + "uptime", + "siem" + ] ] } } @@ -1243,7 +1279,9 @@ "enabledInLicense": { "type": "boolean", "description": "Indicates whether the connector is enabled in the license.", - "example": true + "examples": [ + true + ] }, "id": { "type": "string", @@ -1362,7 +1400,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "parameters": { @@ -1382,7 +1421,9 @@ "required": true, "schema": { "type": "string", - "example": "df770e30-8b8b-11ed-a780-3b746c987a81" + "examples": [ + "df770e30-8b8b-11ed-a780-3b746c987a81" + ] } }, "space_id": { @@ -1392,7 +1433,9 @@ "required": true, "schema": { "type": "string", - "example": "default" + "examples": [ + "default" + ] } }, "action_id": { @@ -1402,7 +1445,9 @@ "required": true, "schema": { "type": "string", - "example": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad" + "examples": [ + "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad" + ] } } }, @@ -1465,12 +1510,16 @@ "enum": [ ".bedrock" ], - "example": ".bedrock" + "examples": [ + ".bedrock" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_bedrock" @@ -1495,11 +1544,13 @@ "createCommentJson": { "type": "string", "description": "A JSON payload sent to the create comment URL to create a case comment. You can use variables to add Kibana Cases data to the payload. The required variable is `case.comment`. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated once the Mustache variables have been placed when the REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass.\n", - "example": { - "body": { - "[object Object]": null + "examples": [ + { + "body": { + "[object Object]": null + } } - } + ] }, "createCommentMethod": { "type": "string", @@ -1514,24 +1565,28 @@ "createCommentUrl": { "type": "string", "description": "The REST API URL to create a case comment by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts setting`, add the hostname to the allowed hosts.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment" + ] }, "createIncidentJson": { "type": "string", "description": "A JSON payload sent to the create case URL to create a case. You can use variables to add case data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review.\n", - "example": { - "fields": { - "summary": { - "[object Object]": null - }, - "description": { - "[object Object]": null - }, - "labels": { - "[object Object]": null + "examples": [ + { + "fields": { + "summary": { + "[object Object]": null + }, + "description": { + "[object Object]": null + }, + "labels": { + "[object Object]": null + } } } - } + ] }, "createIncidentMethod": { "type": "string", @@ -1558,7 +1613,9 @@ "getIncidentUrl": { "type": "string", "description": "The REST API URL to get the case by ID from the third-party system. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. You can use a variable to add the external system ID to the URL. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}" + ] }, "hasAuth": { "type": "boolean", @@ -1572,19 +1629,21 @@ "updateIncidentJson": { "type": "string", "description": "The JSON payload sent to the update case URL to update the case. You can use variables to add Kibana Cases data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review.\n", - "example": { - "fields": { - "summary": { - "[object Object]": null - }, - "description": { - "[object Object]": null - }, - "labels": { - "[object Object]": null + "examples": [ + { + "fields": { + "summary": { + "[object Object]": null + }, + "description": { + "[object Object]": null + }, + "labels": { + "[object Object]": null + } } } - } + ] }, "updateIncidentMethod": { "type": "string", @@ -1599,12 +1658,16 @@ "updateIncidentUrl": { "type": "string", "description": "The REST API URL to update the case by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}}" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}}" + ] }, "viewIncidentUrl": { "type": "string", "description": "The URL to view the case in the external system. You can use variables to add the external system ID or external system title to the URL.\n", - "example": "https://testing-jira.atlassian.net/browse/{{{external.system.title}}}" + "examples": [ + "https://testing-jira.atlassian.net/browse/{{{external.system.title}}}" + ] } } }, @@ -1641,12 +1704,16 @@ "enum": [ ".cases-webhook" ], - "example": ".cases-webhook" + "examples": [ + ".cases-webhook" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_cases_webhook" @@ -1701,12 +1768,16 @@ "enum": [ ".d3security" ], - "example": ".d3security" + "examples": [ + ".d3security" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_d3security" @@ -1723,8 +1794,10 @@ "properties": { "clientId": { "description": "The client identifier, which is a part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "from": { "description": "The from address for all emails sent by the connector. It must be specified in `user@host-name` format.\n", @@ -1740,8 +1813,10 @@ "type": "string" }, "oauthTokenUrl": { - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "port": { "description": "The port to connect to on the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. \n", @@ -1765,8 +1840,10 @@ }, "tenantId": { "description": "The tenant identifier, which is part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -1809,12 +1886,16 @@ "enum": [ ".email" ], - "example": ".email" + "examples": [ + ".email" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_email" @@ -1905,12 +1986,16 @@ "enum": [ ".gen-ai" ], - "example": ".gen-ai" + "examples": [ + ".gen-ai" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_genai" @@ -1928,8 +2013,10 @@ "executionTimeField": { "description": "A field that indicates when the document was indexed.", "default": null, - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "index": { "description": "The Elasticsearch index to be written to.", @@ -1961,12 +2048,16 @@ "enum": [ ".index" ], - "example": ".index" + "examples": [ + ".index" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] } } }, @@ -2028,12 +2119,16 @@ "enum": [ ".jira" ], - "example": ".jira" + "examples": [ + ".jira" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_jira" @@ -2088,12 +2183,16 @@ "enum": [ ".opsgenie" ], - "example": ".opsgenie" + "examples": [ + ".opsgenie" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_opsgenie" @@ -2107,9 +2206,13 @@ "properties": { "apiUrl": { "description": "The PagerDuty event URL.", - "type": "string", - "nullable": true, - "example": "https://events.pagerduty.com/v2/enqueue" + "type": [ + "string", + "null" + ], + "examples": [ + "https://events.pagerduty.com/v2/enqueue" + ] } } }, @@ -2147,12 +2250,16 @@ "enum": [ ".pagerduty" ], - "example": ".pagerduty" + "examples": [ + ".pagerduty" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_pagerduty" @@ -2214,7 +2321,9 @@ "connector_type_id": { "description": "The type of connector.", "type": "string", - "example": ".resilient", + "examples": [ + ".resilient" + ], "enum": [ ".resilient" ] @@ -2222,7 +2331,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_resilient" @@ -2244,12 +2355,16 @@ "enum": [ ".server-log" ], - "example": ".server-log" + "examples": [ + ".server-log" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] } } }, @@ -2336,12 +2451,16 @@ "enum": [ ".servicenow" ], - "example": ".servicenow" + "examples": [ + ".servicenow" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -2399,12 +2518,16 @@ "enum": [ ".servicenow-itom" ], - "example": ".servicenow-itom" + "examples": [ + ".servicenow-itom" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -2431,12 +2554,16 @@ "enum": [ ".servicenow-sir" ], - "example": ".servicenow-sir" + "examples": [ + ".servicenow-sir" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -2473,12 +2600,16 @@ "enum": [ ".slack_api" ], - "example": ".slack_api" + "examples": [ + ".slack_api" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_slack_api" @@ -2515,12 +2646,16 @@ "enum": [ ".slack" ], - "example": ".slack" + "examples": [ + ".slack" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_slack_webhook" @@ -2797,12 +2932,16 @@ "enum": [ ".swimlane" ], - "example": ".swimlane" + "examples": [ + ".swimlane" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_swimlane" @@ -2839,12 +2978,16 @@ "enum": [ ".teams" ], - "example": ".teams" + "examples": [ + ".teams" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_teams" @@ -2904,12 +3047,16 @@ "enum": [ ".tines" ], - "example": ".tines" + "examples": [ + ".tines" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_tines" @@ -2964,12 +3111,16 @@ "enum": [ ".torq" ], - "example": ".torq" + "examples": [ + ".torq" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_torq" @@ -3007,8 +3158,10 @@ "description": "If `true`, a user name and password must be provided for login type authentication.\n" }, "headers": { - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "description": "A set of key-value pairs sent as headers with the request." }, "method": { @@ -3083,12 +3236,16 @@ "enum": [ ".webhook" ], - "example": ".webhook" + "examples": [ + ".webhook" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_webhook" @@ -3102,8 +3259,10 @@ "properties": { "configUrl": { "description": "The request URL for the Elastic Alerts trigger in xMatters. It is applicable only when `usesBasic` is `true`.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "usesBasic": { "description": "Specifies whether the connector uses HTTP basic authentication (`true`) or URL authentication (`false`).", @@ -3151,12 +3310,16 @@ "enum": [ ".xmatters" ], - "example": ".xmatters" + "examples": [ + ".xmatters" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_xmatters" @@ -3241,22 +3404,30 @@ "is_deprecated": { "type": "boolean", "description": "Indicates whether the connector type is deprecated.", - "example": false + "examples": [ + false + ] }, "is_missing_secrets": { "type": "boolean", "description": "Indicates whether secrets are missing for the connector. Secrets configuration properties vary depending on the connector type.", - "example": false + "examples": [ + false + ] }, "is_preconfigured": { "type": "boolean", "description": "Indicates whether it is a preconfigured connector. If true, the `config` and `is_missing_secrets` properties are omitted from the response. \n", - "example": false + "examples": [ + false + ] }, "is_system_action": { "type": "boolean", "description": "Indicates whether the connector is used for system actions.", - "example": false + "examples": [ + false + ] }, "connector_response_properties_bedrock": { "title": "Connector response properties for an Amazon Bedrock connector", @@ -3305,7 +3476,9 @@ "referenced_by_count": { "type": "integer", "description": "Indicates the number of saved objects that reference the connector. If `is_preconfigured` is true, this value is not calculated. This property is returned only by the get all connectors API.\n", - "example": 2 + "examples": [ + 2 + ] }, "connector_response_properties_cases_webhook": { "title": "Connector request properties for a Webhook - Case Management connector", @@ -3687,8 +3860,10 @@ ], "properties": { "config": { - "type": "object", - "nullable": true + "type": [ + "object", + "null" + ] }, "connector_type_id": { "type": "string", @@ -4328,7 +4503,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_cases_webhook" @@ -4581,7 +4758,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_swimlane" @@ -4758,6 +4937,30 @@ } ] }, + "run_connector_params_acknowledge_resolve_pagerduty": { + "title": "PagerDuty connector parameters", + "description": "Test an action that acknowledges or resolves a PagerDuty alert.", + "type": "object", + "required": [ + "dedupKey", + "eventAction" + ], + "properties": { + "dedupKey": { + "description": "The deduplication key for the PagerDuty alert.", + "type": "string", + "maxLength": 255 + }, + "eventAction": { + "description": "The type of event.", + "type": "string", + "enum": [ + "acknowledge", + "resolve" + ] + } + } + }, "run_connector_params_documents": { "title": "Index connector parameters", "description": "Test an action that indexes a document into Elasticsearch.", @@ -4862,6 +5065,95 @@ } } }, + "run_connector_params_trigger_pagerduty": { + "title": "PagerDuty connector parameters", + "description": "Test an action that triggers a PagerDuty alert.", + "type": "object", + "required": [ + "eventAction" + ], + "properties": { + "class": { + "description": "The class or type of the event.", + "type": "string", + "examples": [ + "cpu load" + ] + }, + "component": { + "description": "The component of the source machine that is responsible for the event.", + "type": "string", + "examples": [ + "eth0" + ] + }, + "customDetails": { + "description": "Additional details to add to the event.", + "type": "object" + }, + "dedupKey": { + "description": "All actions sharing this key will be associated with the same PagerDuty alert. This value is used to correlate trigger and resolution.\n", + "type": "string", + "maxLength": 255 + }, + "eventAction": { + "description": "The type of event.", + "type": "string", + "enum": [ + "trigger" + ] + }, + "group": { + "description": "The logical grouping of components of a service.", + "type": "string", + "examples": [ + "app-stack" + ] + }, + "links": { + "description": "A list of links to add to the event.", + "type": "array", + "items": { + "type": "object", + "properties": { + "href": { + "description": "The URL for the link.", + "type": "string" + }, + "text": { + "description": "A plain text description of the purpose of the link.", + "type": "string" + } + } + } + }, + "severity": { + "description": "The severity of the event on the affected system.", + "type": "string", + "enum": [ + "critical", + "error", + "info", + "warning" + ], + "default": "info" + }, + "source": { + "description": "The affected system, such as a hostname or fully qualified domain name. Defaults to the Kibana saved object id of the action.\n", + "type": "string" + }, + "summary": { + "description": "A summery of the event.", + "type": "string", + "maxLength": 1024 + }, + "timestamp": { + "description": "An ISO-8601 timestamp that indicates when the event was detected or generated.", + "type": "string", + "format": "date-time" + } + } + }, "run_connector_subaction_addevent": { "title": "The addEvent subaction", "type": "object", @@ -5012,10 +5304,12 @@ "type": "object", "description": "The custom properties of the alert.", "additionalProperties": true, - "example": { - "key1": "value1", - "key2": "value2" - } + "examples": [ + { + "key1": "value1", + "key2": "value2" + } + ] }, "entity": { "type": "string", @@ -5147,7 +5441,9 @@ "id": { "type": "string", "description": "The Jira issue type identifier.", - "example": 10024 + "examples": [ + 10024 + ] } } } @@ -5229,7 +5525,9 @@ "externalId": { "type": "string", "description": "The Jira, ServiceNow ITSM, or ServiceNow SecOps issue identifier.", - "example": 71778 + "examples": [ + 71778 + ] } } } @@ -5259,7 +5557,9 @@ "id": { "type": "string", "description": "The Jira issue identifier.", - "example": 71778 + "examples": [ + 71778 + ] } } } @@ -5509,6 +5809,9 @@ "properties": { "params": { "oneOf": [ + { + "$ref": "#/components/schemas/run_connector_params_acknowledge_resolve_pagerduty" + }, { "$ref": "#/components/schemas/run_connector_params_documents" }, @@ -5518,6 +5821,9 @@ { "$ref": "#/components/schemas/run_connector_params_message_serverlog" }, + { + "$ref": "#/components/schemas/run_connector_params_trigger_pagerduty" + }, { "title": "Subaction parameters", "description": "Test an action that involves a subaction.", @@ -5603,7 +5909,9 @@ ".webhook", ".xmatters" ], - "example": ".server-log" + "examples": [ + ".server-log" + ] }, "action_response_properties": { "title": "Action response properties", @@ -5838,6 +6146,24 @@ } } }, + "run_pagerduty_connector_request": { + "summary": "Run a PagerDuty connector to trigger an alert.", + "value": { + "params": { + "eventAction": "trigger", + "summary": "A brief event summary", + "links": [ + { + "href": "http://example.com/pagerduty", + "text": "An example link" + } + ], + "customDetails": { + "my_data_1": "test data" + } + } + } + }, "run_server_log_connector_request": { "summary": "Run a server log connector.", "value": { @@ -5974,6 +6300,18 @@ "status": "ok" } }, + "run_pagerduty_connector_response": { + "summary": "Response from running a PagerDuty connector.", + "value": { + "connector_id": "45de9f70-954f-4608-b12a-db7cf808e49d", + "data": { + "dedup_key": "5115e138b26b484a81eaea779faa6016", + "message": "Event processed", + "status": "success" + }, + "status": "ok" + } + }, "run_server_log_connector_response": { "summary": "Response from running a server log connector.", "value": { @@ -6176,7 +6514,9 @@ "properties": { "error": { "type": "string", - "example": "Unauthorized", + "examples": [ + "Unauthorized" + ], "enum": [ "Unauthorized" ] @@ -6186,7 +6526,9 @@ }, "statusCode": { "type": "integer", - "example": 401, + "examples": [ + 401 + ], "enum": [ 401 ] @@ -6206,18 +6548,24 @@ "properties": { "error": { "type": "string", - "example": "Not Found", + "examples": [ + "Not Found" + ], "enum": [ "Not Found" ] }, "message": { "type": "string", - "example": "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" + "examples": [ + "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" + ] }, "statusCode": { "type": "integer", - "example": 404, + "examples": [ + 404 + ], "enum": [ 404 ] diff --git a/x-pack/plugins/actions/docs/openapi/bundled.yaml b/x-pack/plugins/actions/docs/openapi/bundled.yaml index 481d0f0a7ec42..50856a35f9399 100644 --- a/x-pack/plugins/actions/docs/openapi/bundled.yaml +++ b/x-pack/plugins/actions/docs/openapi/bundled.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Connectors description: OpenAPI schema for Connectors endpoints - version: '0.1' + version: '0.2' contact: name: Connectors Team license: @@ -109,7 +109,8 @@ paths: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: @@ -185,6 +186,8 @@ paths: $ref: '#/components/examples/run_index_connector_request' runJiraConnectorRequest: $ref: '#/components/examples/run_jira_connector_request' + runPagerDutyConnectorRequest: + $ref: '#/components/examples/run_pagerduty_connector_request' runServerLogConnectorRequest: $ref: '#/components/examples/run_server_log_connector_request' runServiceNowITOMConnectorRequest: @@ -227,6 +230,8 @@ paths: $ref: '#/components/examples/run_index_connector_response' runJiraConnectorResponse: $ref: '#/components/examples/run_jira_connector_response' + runPagerDutyConnectorResponse: + $ref: '#/components/examples/run_pagerduty_connector_response' runServerLogConnectorResponse: $ref: '#/components/examples/run_server_log_connector_response' runServiceNowITOMConnectorResponse: @@ -282,37 +287,43 @@ paths: enabled: type: boolean description: Indicates whether the connector type is enabled in Kibana. - example: true + examples: + - true enabled_in_config: type: boolean description: Indicates whether the connector type is enabled in the Kibana configuration file. - example: true + examples: + - true enabled_in_license: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: $ref: '#/components/schemas/connector_types' is_system_action_type: type: boolean - example: false + examples: + - false minimum_license_required: type: string description: The license that is required to use the connector type. - example: basic + examples: + - basic name: type: string description: The name of the connector type. - example: Index + examples: + - Index supported_feature_ids: type: array description: The features that are supported by the connector type. items: $ref: '#/components/schemas/features' - example: - - alerting - - cases - - siem + examples: + - - alerting + - cases + - siem examples: getConnectorTypesServerlessResponse: $ref: '#/components/examples/get_connector_types_generativeai_response' @@ -421,7 +432,8 @@ paths: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: @@ -530,34 +542,39 @@ paths: enabled: type: boolean description: Indicates whether the connector type is enabled in Kibana. - example: true + examples: + - true enabled_in_config: type: boolean description: Indicates whether the connector type is enabled in the Kibana `.yml` file. - example: true + examples: + - true enabled_in_license: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: $ref: '#/components/schemas/connector_types' minimum_license_required: type: string description: The license that is required to use the connector type. - example: basic + examples: + - basic name: type: string description: The name of the connector type. - example: Index + examples: + - Index supported_feature_ids: type: array description: The Kibana features that are supported by the connector type. items: $ref: '#/components/schemas/features' - example: - - alerting - - uptime - - siem + examples: + - - alerting + - uptime + - siem examples: getConnectorTypesResponse: $ref: '#/components/examples/get_connector_types_response' @@ -789,7 +806,8 @@ paths: enabledInLicense: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: type: string description: The unique identifier for the connector type. @@ -859,7 +877,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' parameters: kbn_xsrf: schema: @@ -875,7 +894,8 @@ components: required: true schema: type: string - example: df770e30-8b8b-11ed-a780-3b746c987a81 + examples: + - df770e30-8b8b-11ed-a780-3b746c987a81 space_id: in: path name: spaceId @@ -883,7 +903,8 @@ components: required: true schema: type: string - example: default + examples: + - default action_id: in: path name: actionId @@ -891,7 +912,8 @@ components: required: true schema: type: string - example: c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad + examples: + - c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad schemas: config_properties_bedrock: title: Connector request properties for an Amazon Bedrock connector @@ -939,11 +961,13 @@ components: description: The type of connector. enum: - .bedrock - example: .bedrock + examples: + - .bedrock name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_bedrock' config_properties_cases_webhook: @@ -964,9 +988,9 @@ components: type: string description: | A JSON payload sent to the create comment URL to create a case comment. You can use variables to add Kibana Cases data to the payload. The required variable is `case.comment`. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated once the Mustache variables have been placed when the REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: - body: - '[object Object]': null + examples: + - body: + '[object Object]': null createCommentMethod: type: string description: | @@ -980,19 +1004,20 @@ components: type: string description: | The REST API URL to create a case comment by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts setting`, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment createIncidentJson: type: string description: | A JSON payload sent to the create case URL to create a case. You can use variables to add case data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: - fields: - summary: - '[object Object]': null - description: - '[object Object]': null - labels: - '[object Object]': null + examples: + - fields: + summary: + '[object Object]': null + description: + '[object Object]': null + labels: + '[object Object]': null createIncidentMethod: type: string description: | @@ -1016,7 +1041,8 @@ components: type: string description: | The REST API URL to get the case by ID from the third-party system. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. You can use a variable to add the external system ID to the URL. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} hasAuth: type: boolean description: If true, a username and password for login type authentication must be provided. @@ -1029,14 +1055,14 @@ components: type: string description: | The JSON payload sent to the update case URL to update the case. You can use variables to add Kibana Cases data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: - fields: - summary: - '[object Object]': null - description: - '[object Object]': null - labels: - '[object Object]': null + examples: + - fields: + summary: + '[object Object]': null + description: + '[object Object]': null + labels: + '[object Object]': null updateIncidentMethod: type: string description: | @@ -1050,12 +1076,14 @@ components: type: string description: | The REST API URL to update the case by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} viewIncidentUrl: type: string description: | The URL to view the case in the external system. You can use variables to add the external system ID or external system title to the URL. - example: https://testing-jira.atlassian.net/browse/{{{external.system.title}}} + examples: + - https://testing-jira.atlassian.net/browse/{{{external.system.title}}} secrets_properties_cases_webhook: title: Connector secrets properties for Webhook - Case Management connector type: object @@ -1083,11 +1111,13 @@ components: description: The type of connector. enum: - .cases-webhook - example: .cases-webhook + examples: + - .cases-webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_cases_webhook' config_properties_d3security: @@ -1129,11 +1159,13 @@ components: description: The type of connector. enum: - .d3security - example: .d3security + examples: + - .d3security name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_d3security' config_properties_email: @@ -1146,8 +1178,9 @@ components: clientId: description: | The client identifier, which is a part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true + type: + - string + - 'null' from: description: | The from address for all emails sent by the connector. It must be specified in `user@host-name` format. @@ -1162,8 +1195,9 @@ components: The host name of the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. type: string oauthTokenUrl: - type: string - nullable: true + type: + - string + - 'null' port: description: | The port to connect to on the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. @@ -1186,8 +1220,9 @@ components: tenantId: description: | The tenant identifier, which is part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true + type: + - string + - 'null' secrets_properties_email: title: Connector secrets properties for an email connector description: Defines secrets for connectors when type is `.email`. @@ -1223,11 +1258,13 @@ components: description: The type of connector. enum: - .email - example: .email + examples: + - .email name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_email' config_properties_genai: @@ -1291,11 +1328,13 @@ components: description: The type of connector. enum: - .gen-ai - example: .gen-ai + examples: + - .gen-ai name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_genai' config_properties_index: @@ -1308,8 +1347,9 @@ components: executionTimeField: description: A field that indicates when the document was indexed. default: null - type: string - nullable: true + type: + - string + - 'null' index: description: The Elasticsearch index to be written to. type: string @@ -1334,11 +1374,13 @@ components: description: The type of connector. enum: - .index - example: .index + examples: + - .index name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector config_properties_jira: title: Connector request properties for a Jira connector required: @@ -1384,11 +1426,13 @@ components: description: The type of connector. enum: - .jira - example: .jira + examples: + - .jira name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_jira' config_properties_opsgenie: @@ -1429,11 +1473,13 @@ components: description: The type of connector. enum: - .opsgenie - example: .opsgenie + examples: + - .opsgenie name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_opsgenie' config_properties_pagerduty: @@ -1443,9 +1489,11 @@ components: properties: apiUrl: description: The PagerDuty event URL. - type: string - nullable: true - example: https://events.pagerduty.com/v2/enqueue + type: + - string + - 'null' + examples: + - https://events.pagerduty.com/v2/enqueue secrets_properties_pagerduty: title: Connector secrets properties for a PagerDuty connector description: Defines secrets for connectors when type is `.pagerduty`. @@ -1475,11 +1523,13 @@ components: description: The type of connector. enum: - .pagerduty - example: .pagerduty + examples: + - .pagerduty name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_pagerduty' config_properties_resilient: @@ -1525,13 +1575,15 @@ components: connector_type_id: description: The type of connector. type: string - example: .resilient + examples: + - .resilient enum: - .resilient name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_resilient' create_connector_request_serverlog: @@ -1547,11 +1599,13 @@ components: description: The type of connector. enum: - .server-log - example: .server-log + examples: + - .server-log name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector config_properties_servicenow: title: Connector request properties for a ServiceNow ITSM connector required: @@ -1622,11 +1676,13 @@ components: description: The type of connector. enum: - .servicenow - example: .servicenow + examples: + - .servicenow name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' config_properties_servicenow_itom: @@ -1674,11 +1730,13 @@ components: description: The type of connector. enum: - .servicenow-itom - example: .servicenow-itom + examples: + - .servicenow-itom name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' create_connector_request_servicenow_sir: @@ -1699,11 +1757,13 @@ components: description: The type of connector. enum: - .servicenow-sir - example: .servicenow-sir + examples: + - .servicenow-sir name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' secrets_properties_slack_api: @@ -1730,11 +1790,13 @@ components: description: The type of connector. enum: - .slack_api - example: .slack_api + examples: + - .slack_api name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_slack_api' secrets_properties_slack_webhook: @@ -1761,11 +1823,13 @@ components: description: The type of connector. enum: - .slack - example: .slack + examples: + - .slack name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_slack_webhook' config_properties_swimlane: @@ -1974,11 +2038,13 @@ components: description: The type of connector. enum: - .swimlane - example: .swimlane + examples: + - .swimlane name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_swimlane' secrets_properties_teams: @@ -2006,11 +2072,13 @@ components: description: The type of connector. enum: - .teams - example: .teams + examples: + - .teams name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_teams' config_properties_tines: @@ -2056,11 +2124,13 @@ components: description: The type of connector. enum: - .tines - example: .tines + examples: + - .tines name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_tines' config_properties_torq: @@ -2101,11 +2171,13 @@ components: description: The type of connector. enum: - .torq - example: .torq + examples: + - .torq name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_torq' config_properties_webhook: @@ -2137,8 +2209,9 @@ components: description: | If `true`, a user name and password must be provided for login type authentication. headers: - type: object - nullable: true + type: + - object + - 'null' description: A set of key-value pairs sent as headers with the request. method: type: string @@ -2201,11 +2274,13 @@ components: description: The type of connector. enum: - .webhook - example: .webhook + examples: + - .webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_webhook' config_properties_xmatters: @@ -2216,8 +2291,9 @@ components: configUrl: description: | The request URL for the Elastic Alerts trigger in xMatters. It is applicable only when `usesBasic` is `true`. - type: string - nullable: true + type: + - string + - 'null' usesBasic: description: Specifies whether the connector uses HTTP basic authentication (`true`) or URL authentication (`false`). type: boolean @@ -2257,11 +2333,13 @@ components: description: The type of connector. enum: - .xmatters - example: .xmatters + examples: + - .xmatters name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_xmatters' create_connector_request: @@ -2295,20 +2373,24 @@ components: is_deprecated: type: boolean description: Indicates whether the connector type is deprecated. - example: false + examples: + - false is_missing_secrets: type: boolean description: Indicates whether secrets are missing for the connector. Secrets configuration properties vary depending on the connector type. - example: false + examples: + - false is_preconfigured: type: boolean description: | Indicates whether it is a preconfigured connector. If true, the `config` and `is_missing_secrets` properties are omitted from the response. - example: false + examples: + - false is_system_action: type: boolean description: Indicates whether the connector is used for system actions. - example: false + examples: + - false connector_response_properties_bedrock: title: Connector response properties for an Amazon Bedrock connector type: object @@ -2345,7 +2427,8 @@ components: type: integer description: | Indicates the number of saved objects that reference the connector. If `is_preconfigured` is true, this value is not calculated. This property is returned only by the get all connectors API. - example: 2 + examples: + - 2 connector_response_properties_cases_webhook: title: Connector request properties for a Webhook - Case Management connector type: object @@ -2621,8 +2704,9 @@ components: - name properties: config: - type: object - nullable: true + type: + - object + - 'null' connector_type_id: type: string description: The type of connector. @@ -3056,7 +3140,8 @@ components: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_cases_webhook' update_connector_request_d3security: @@ -3236,7 +3321,8 @@ components: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_swimlane' update_connector_request_teams: @@ -3336,6 +3422,24 @@ components: - $ref: '#/components/schemas/update_connector_request_torq' - $ref: '#/components/schemas/update_connector_request_webhook' - $ref: '#/components/schemas/update_connector_request_xmatters' + run_connector_params_acknowledge_resolve_pagerduty: + title: PagerDuty connector parameters + description: Test an action that acknowledges or resolves a PagerDuty alert. + type: object + required: + - dedupKey + - eventAction + properties: + dedupKey: + description: The deduplication key for the PagerDuty alert. + type: string + maxLength: 255 + eventAction: + description: The type of event. + type: string + enum: + - acknowledge + - resolve run_connector_params_documents: title: Index connector parameters description: Test an action that indexes a document into Elasticsearch. @@ -3413,6 +3517,74 @@ components: message: type: string description: The message for server log connectors. + run_connector_params_trigger_pagerduty: + title: PagerDuty connector parameters + description: Test an action that triggers a PagerDuty alert. + type: object + required: + - eventAction + properties: + class: + description: The class or type of the event. + type: string + examples: + - cpu load + component: + description: The component of the source machine that is responsible for the event. + type: string + examples: + - eth0 + customDetails: + description: Additional details to add to the event. + type: object + dedupKey: + description: | + All actions sharing this key will be associated with the same PagerDuty alert. This value is used to correlate trigger and resolution. + type: string + maxLength: 255 + eventAction: + description: The type of event. + type: string + enum: + - trigger + group: + description: The logical grouping of components of a service. + type: string + examples: + - app-stack + links: + description: A list of links to add to the event. + type: array + items: + type: object + properties: + href: + description: The URL for the link. + type: string + text: + description: A plain text description of the purpose of the link. + type: string + severity: + description: The severity of the event on the affected system. + type: string + enum: + - critical + - error + - info + - warning + default: info + source: + description: | + The affected system, such as a hostname or fully qualified domain name. Defaults to the Kibana saved object id of the action. + type: string + summary: + description: A summery of the event. + type: string + maxLength: 1024 + timestamp: + description: An ISO-8601 timestamp that indicates when the event was detected or generated. + type: string + format: date-time run_connector_subaction_addevent: title: The addEvent subaction type: object @@ -3525,9 +3697,9 @@ components: type: object description: The custom properties of the alert. additionalProperties: true - example: - key1: value1 - key2: value2 + examples: + - key1: value1 + key2: value2 entity: type: string description: The domain of the alert. For example, the application or server name. @@ -3625,7 +3797,8 @@ components: id: type: string description: The Jira issue type identifier. - example: 10024 + examples: + - 10024 run_connector_subaction_getchoices: title: The getChoices subaction type: object @@ -3683,7 +3856,8 @@ components: externalId: type: string description: The Jira, ServiceNow ITSM, or ServiceNow SecOps issue identifier. - example: 71778 + examples: + - 71778 run_connector_subaction_issue: title: The issue subaction type: object @@ -3704,7 +3878,8 @@ components: id: type: string description: The Jira issue identifier. - example: 71778 + examples: + - 71778 run_connector_subaction_issues: title: The issues subaction type: object @@ -3877,9 +4052,11 @@ components: properties: params: oneOf: + - $ref: '#/components/schemas/run_connector_params_acknowledge_resolve_pagerduty' - $ref: '#/components/schemas/run_connector_params_documents' - $ref: '#/components/schemas/run_connector_params_message_email' - $ref: '#/components/schemas/run_connector_params_message_serverlog' + - $ref: '#/components/schemas/run_connector_params_trigger_pagerduty' - title: Subaction parameters description: Test an action that involves a subaction. oneOf: @@ -3933,7 +4110,8 @@ components: - .torq - .webhook - .xmatters - example: .server-log + examples: + - .server-log action_response_properties: title: Action response properties description: The properties vary depending on the action type. @@ -4110,6 +4288,17 @@ components: value: params: subAction: issueTypes + run_pagerduty_connector_request: + summary: Run a PagerDuty connector to trigger an alert. + value: + params: + eventAction: trigger + summary: A brief event summary + links: + - href: http://example.com/pagerduty + text: An example link + customDetails: + my_data_1: test data run_server_log_connector_request: summary: Run a server log connector. value: @@ -4201,6 +4390,15 @@ components: - id: 10000 name: Epic status: ok + run_pagerduty_connector_response: + summary: Response from running a PagerDuty connector. + value: + connector_id: 45de9f70-954f-4608-b12a-db7cf808e49d + data: + dedup_key: 5115e138b26b484a81eaea779faa6016 + message: Event processed + status: success + status: ok run_server_log_connector_response: summary: Response from running a server log connector. value: @@ -4347,14 +4545,16 @@ components: properties: error: type: string - example: Unauthorized + examples: + - Unauthorized enum: - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 enum: - 401 '404': @@ -4367,15 +4567,18 @@ components: properties: error: type: string - example: Not Found + examples: + - Not Found enum: - Not Found message: type: string - example: Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found + examples: + - Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found statusCode: type: integer - example: 404 + examples: + - 404 enum: - 404 200_actions: diff --git a/x-pack/plugins/actions/docs/openapi/bundled_serverless.json b/x-pack/plugins/actions/docs/openapi/bundled_serverless.json index dadcc4cc08767..769fdf2d258e9 100644 --- a/x-pack/plugins/actions/docs/openapi/bundled_serverless.json +++ b/x-pack/plugins/actions/docs/openapi/bundled_serverless.json @@ -1,9 +1,9 @@ { - "openapi": "3.0.1", + "openapi": "3.1.0", "info": { "title": "Connectors", "description": "OpenAPI schema for connectors in Serverless projects", - "version": "0.1", + "version": "0.2", "contact": { "name": "Connectors Team" }, @@ -180,7 +180,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } } ], @@ -336,34 +338,46 @@ "enabled": { "type": "boolean", "description": "Indicates whether the connector type is enabled in Kibana.", - "example": true + "examples": [ + true + ] }, "enabled_in_config": { "type": "boolean", "description": "Indicates whether the connector type is enabled in the Kibana configuration file.", - "example": true + "examples": [ + true + ] }, "enabled_in_license": { "type": "boolean", "description": "Indicates whether the connector is enabled in the license.", - "example": true + "examples": [ + true + ] }, "id": { "$ref": "#/components/schemas/connector_types" }, "is_system_action_type": { "type": "boolean", - "example": false + "examples": [ + false + ] }, "minimum_license_required": { "type": "string", "description": "The license that is required to use the connector type.", - "example": "basic" + "examples": [ + "basic" + ] }, "name": { "type": "string", "description": "The name of the connector type.", - "example": "Index" + "examples": [ + "Index" + ] }, "supported_feature_ids": { "type": "array", @@ -371,10 +385,12 @@ "items": { "$ref": "#/components/schemas/features" }, - "example": [ - "alerting", - "cases", - "siem" + "examples": [ + [ + "alerting", + "cases", + "siem" + ] ] } } @@ -400,7 +416,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "parameters": { @@ -420,7 +437,9 @@ "required": true, "schema": { "type": "string", - "example": "df770e30-8b8b-11ed-a780-3b746c987a81" + "examples": [ + "df770e30-8b8b-11ed-a780-3b746c987a81" + ] } } }, @@ -483,12 +502,16 @@ "enum": [ ".bedrock" ], - "example": ".bedrock" + "examples": [ + ".bedrock" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_bedrock" @@ -513,11 +536,13 @@ "createCommentJson": { "type": "string", "description": "A JSON payload sent to the create comment URL to create a case comment. You can use variables to add Kibana Cases data to the payload. The required variable is `case.comment`. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated once the Mustache variables have been placed when the REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass.\n", - "example": { - "body": { - "[object Object]": null + "examples": [ + { + "body": { + "[object Object]": null + } } - } + ] }, "createCommentMethod": { "type": "string", @@ -532,24 +557,28 @@ "createCommentUrl": { "type": "string", "description": "The REST API URL to create a case comment by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts setting`, add the hostname to the allowed hosts.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment" + ] }, "createIncidentJson": { "type": "string", "description": "A JSON payload sent to the create case URL to create a case. You can use variables to add case data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review.\n", - "example": { - "fields": { - "summary": { - "[object Object]": null - }, - "description": { - "[object Object]": null - }, - "labels": { - "[object Object]": null + "examples": [ + { + "fields": { + "summary": { + "[object Object]": null + }, + "description": { + "[object Object]": null + }, + "labels": { + "[object Object]": null + } } } - } + ] }, "createIncidentMethod": { "type": "string", @@ -576,7 +605,9 @@ "getIncidentUrl": { "type": "string", "description": "The REST API URL to get the case by ID from the third-party system. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. You can use a variable to add the external system ID to the URL. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}" + ] }, "hasAuth": { "type": "boolean", @@ -590,19 +621,21 @@ "updateIncidentJson": { "type": "string", "description": "The JSON payload sent to the update case URL to update the case. You can use variables to add Kibana Cases data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review.\n", - "example": { - "fields": { - "summary": { - "[object Object]": null - }, - "description": { - "[object Object]": null - }, - "labels": { - "[object Object]": null + "examples": [ + { + "fields": { + "summary": { + "[object Object]": null + }, + "description": { + "[object Object]": null + }, + "labels": { + "[object Object]": null + } } } - } + ] }, "updateIncidentMethod": { "type": "string", @@ -617,12 +650,16 @@ "updateIncidentUrl": { "type": "string", "description": "The REST API URL to update the case by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts.\n", - "example": "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}}" + "examples": [ + "https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}}" + ] }, "viewIncidentUrl": { "type": "string", "description": "The URL to view the case in the external system. You can use variables to add the external system ID or external system title to the URL.\n", - "example": "https://testing-jira.atlassian.net/browse/{{{external.system.title}}}" + "examples": [ + "https://testing-jira.atlassian.net/browse/{{{external.system.title}}}" + ] } } }, @@ -659,12 +696,16 @@ "enum": [ ".cases-webhook" ], - "example": ".cases-webhook" + "examples": [ + ".cases-webhook" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_cases_webhook" @@ -719,12 +760,16 @@ "enum": [ ".d3security" ], - "example": ".d3security" + "examples": [ + ".d3security" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_d3security" @@ -741,8 +786,10 @@ "properties": { "clientId": { "description": "The client identifier, which is a part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "from": { "description": "The from address for all emails sent by the connector. It must be specified in `user@host-name` format.\n", @@ -758,8 +805,10 @@ "type": "string" }, "oauthTokenUrl": { - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "port": { "description": "The port to connect to on the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. \n", @@ -783,8 +832,10 @@ }, "tenantId": { "description": "The tenant identifier, which is part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -827,12 +878,16 @@ "enum": [ ".email" ], - "example": ".email" + "examples": [ + ".email" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_email" @@ -923,12 +978,16 @@ "enum": [ ".gen-ai" ], - "example": ".gen-ai" + "examples": [ + ".gen-ai" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_genai" @@ -946,8 +1005,10 @@ "executionTimeField": { "description": "A field that indicates when the document was indexed.", "default": null, - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "index": { "description": "The Elasticsearch index to be written to.", @@ -979,12 +1040,16 @@ "enum": [ ".index" ], - "example": ".index" + "examples": [ + ".index" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] } } }, @@ -1046,12 +1111,16 @@ "enum": [ ".jira" ], - "example": ".jira" + "examples": [ + ".jira" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_jira" @@ -1106,12 +1175,16 @@ "enum": [ ".opsgenie" ], - "example": ".opsgenie" + "examples": [ + ".opsgenie" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_opsgenie" @@ -1125,9 +1198,13 @@ "properties": { "apiUrl": { "description": "The PagerDuty event URL.", - "type": "string", - "nullable": true, - "example": "https://events.pagerduty.com/v2/enqueue" + "type": [ + "string", + "null" + ], + "examples": [ + "https://events.pagerduty.com/v2/enqueue" + ] } } }, @@ -1165,12 +1242,16 @@ "enum": [ ".pagerduty" ], - "example": ".pagerduty" + "examples": [ + ".pagerduty" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_pagerduty" @@ -1232,7 +1313,9 @@ "connector_type_id": { "description": "The type of connector.", "type": "string", - "example": ".resilient", + "examples": [ + ".resilient" + ], "enum": [ ".resilient" ] @@ -1240,7 +1323,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_resilient" @@ -1262,12 +1347,16 @@ "enum": [ ".server-log" ], - "example": ".server-log" + "examples": [ + ".server-log" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] } } }, @@ -1354,12 +1443,16 @@ "enum": [ ".servicenow" ], - "example": ".servicenow" + "examples": [ + ".servicenow" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -1417,12 +1510,16 @@ "enum": [ ".servicenow-itom" ], - "example": ".servicenow-itom" + "examples": [ + ".servicenow-itom" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -1449,12 +1546,16 @@ "enum": [ ".servicenow-sir" ], - "example": ".servicenow-sir" + "examples": [ + ".servicenow-sir" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_servicenow" @@ -1491,12 +1592,16 @@ "enum": [ ".slack_api" ], - "example": ".slack_api" + "examples": [ + ".slack_api" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_slack_api" @@ -1533,12 +1638,16 @@ "enum": [ ".slack" ], - "example": ".slack" + "examples": [ + ".slack" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_slack_webhook" @@ -1815,12 +1924,16 @@ "enum": [ ".swimlane" ], - "example": ".swimlane" + "examples": [ + ".swimlane" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_swimlane" @@ -1857,12 +1970,16 @@ "enum": [ ".teams" ], - "example": ".teams" + "examples": [ + ".teams" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_teams" @@ -1922,12 +2039,16 @@ "enum": [ ".tines" ], - "example": ".tines" + "examples": [ + ".tines" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_tines" @@ -1982,12 +2103,16 @@ "enum": [ ".torq" ], - "example": ".torq" + "examples": [ + ".torq" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_torq" @@ -2025,8 +2150,10 @@ "description": "If `true`, a user name and password must be provided for login type authentication.\n" }, "headers": { - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "description": "A set of key-value pairs sent as headers with the request." }, "method": { @@ -2101,12 +2228,16 @@ "enum": [ ".webhook" ], - "example": ".webhook" + "examples": [ + ".webhook" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_webhook" @@ -2120,8 +2251,10 @@ "properties": { "configUrl": { "description": "The request URL for the Elastic Alerts trigger in xMatters. It is applicable only when `usesBasic` is `true`.\n", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "usesBasic": { "description": "Specifies whether the connector uses HTTP basic authentication (`true`) or URL authentication (`false`).", @@ -2169,12 +2302,16 @@ "enum": [ ".xmatters" ], - "example": ".xmatters" + "examples": [ + ".xmatters" + ] }, "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_xmatters" @@ -2259,22 +2396,30 @@ "is_deprecated": { "type": "boolean", "description": "Indicates whether the connector type is deprecated.", - "example": false + "examples": [ + false + ] }, "is_missing_secrets": { "type": "boolean", "description": "Indicates whether secrets are missing for the connector. Secrets configuration properties vary depending on the connector type.", - "example": false + "examples": [ + false + ] }, "is_preconfigured": { "type": "boolean", "description": "Indicates whether it is a preconfigured connector. If true, the `config` and `is_missing_secrets` properties are omitted from the response. \n", - "example": false + "examples": [ + false + ] }, "is_system_action": { "type": "boolean", "description": "Indicates whether the connector is used for system actions.", - "example": false + "examples": [ + false + ] }, "connector_response_properties_bedrock": { "title": "Connector response properties for an Amazon Bedrock connector", @@ -2323,7 +2468,9 @@ "referenced_by_count": { "type": "integer", "description": "Indicates the number of saved objects that reference the connector. If `is_preconfigured` is true, this value is not calculated. This property is returned only by the get all connectors API.\n", - "example": 2 + "examples": [ + 2 + ] }, "connector_response_properties_cases_webhook": { "title": "Connector request properties for a Webhook - Case Management connector", @@ -2705,8 +2852,10 @@ ], "properties": { "config": { - "type": "object", - "nullable": true + "type": [ + "object", + "null" + ] }, "connector_type_id": { "type": "string", @@ -3346,7 +3495,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_cases_webhook" @@ -3599,7 +3750,9 @@ "name": { "type": "string", "description": "The display name for the connector.", - "example": "my-connector" + "examples": [ + "my-connector" + ] }, "secrets": { "$ref": "#/components/schemas/secrets_properties_swimlane" @@ -3815,7 +3968,9 @@ ".webhook", ".xmatters" ], - "example": ".server-log" + "examples": [ + ".server-log" + ] } }, "examples": { @@ -4048,7 +4203,9 @@ "properties": { "error": { "type": "string", - "example": "Unauthorized", + "examples": [ + "Unauthorized" + ], "enum": [ "Unauthorized" ] @@ -4058,7 +4215,9 @@ }, "statusCode": { "type": "integer", - "example": 401, + "examples": [ + 401 + ], "enum": [ 401 ] @@ -4078,18 +4237,24 @@ "properties": { "error": { "type": "string", - "example": "Not Found", + "examples": [ + "Not Found" + ], "enum": [ "Not Found" ] }, "message": { "type": "string", - "example": "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" + "examples": [ + "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" + ] }, "statusCode": { "type": "integer", - "example": 404, + "examples": [ + 404 + ], "enum": [ 404 ] diff --git a/x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml b/x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml index fbe684c35fe48..15a9dfb0be293 100644 --- a/x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml +++ b/x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Connectors description: OpenAPI schema for connectors in Serverless projects - version: '0.1' + version: '0.2' contact: name: Connectors Team license: @@ -111,7 +111,8 @@ paths: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: @@ -210,37 +211,43 @@ paths: enabled: type: boolean description: Indicates whether the connector type is enabled in Kibana. - example: true + examples: + - true enabled_in_config: type: boolean description: Indicates whether the connector type is enabled in the Kibana configuration file. - example: true + examples: + - true enabled_in_license: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: $ref: '#/components/schemas/connector_types' is_system_action_type: type: boolean - example: false + examples: + - false minimum_license_required: type: string description: The license that is required to use the connector type. - example: basic + examples: + - basic name: type: string description: The name of the connector type. - example: Index + examples: + - Index supported_feature_ids: type: array description: The features that are supported by the connector type. items: $ref: '#/components/schemas/features' - example: - - alerting - - cases - - siem + examples: + - - alerting + - cases + - siem examples: getConnectorTypesServerlessResponse: $ref: '#/components/examples/get_connector_types_generativeai_response' @@ -251,7 +258,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' parameters: kbn_xsrf: schema: @@ -267,7 +275,8 @@ components: required: true schema: type: string - example: df770e30-8b8b-11ed-a780-3b746c987a81 + examples: + - df770e30-8b8b-11ed-a780-3b746c987a81 schemas: config_properties_bedrock: title: Connector request properties for an Amazon Bedrock connector @@ -315,11 +324,13 @@ components: description: The type of connector. enum: - .bedrock - example: .bedrock + examples: + - .bedrock name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_bedrock' config_properties_cases_webhook: @@ -340,9 +351,9 @@ components: type: string description: | A JSON payload sent to the create comment URL to create a case comment. You can use variables to add Kibana Cases data to the payload. The required variable is `case.comment`. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated once the Mustache variables have been placed when the REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: - body: - '[object Object]': null + examples: + - body: + '[object Object]': null createCommentMethod: type: string description: | @@ -356,19 +367,20 @@ components: type: string description: | The REST API URL to create a case comment by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts setting`, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment createIncidentJson: type: string description: | A JSON payload sent to the create case URL to create a case. You can use variables to add case data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: - fields: - summary: - '[object Object]': null - description: - '[object Object]': null - labels: - '[object Object]': null + examples: + - fields: + summary: + '[object Object]': null + description: + '[object Object]': null + labels: + '[object Object]': null createIncidentMethod: type: string description: | @@ -392,7 +404,8 @@ components: type: string description: | The REST API URL to get the case by ID from the third-party system. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. You can use a variable to add the external system ID to the URL. Due to Mustache template variables (the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} hasAuth: type: boolean description: If true, a username and password for login type authentication must be provided. @@ -405,14 +418,14 @@ components: type: string description: | The JSON payload sent to the update case URL to update the case. You can use variables to add Kibana Cases data to the payload. Required variables are `case.title` and `case.description`. Due to Mustache template variables (which is the text enclosed in triple braces, for example, `{{{case.title}}}`), the JSON is not validated when you create the connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: - fields: - summary: - '[object Object]': null - description: - '[object Object]': null - labels: - '[object Object]': null + examples: + - fields: + summary: + '[object Object]': null + description: + '[object Object]': null + labels: + '[object Object]': null updateIncidentMethod: type: string description: | @@ -426,12 +439,14 @@ components: type: string description: | The REST API URL to update the case by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} viewIncidentUrl: type: string description: | The URL to view the case in the external system. You can use variables to add the external system ID or external system title to the URL. - example: https://testing-jira.atlassian.net/browse/{{{external.system.title}}} + examples: + - https://testing-jira.atlassian.net/browse/{{{external.system.title}}} secrets_properties_cases_webhook: title: Connector secrets properties for Webhook - Case Management connector type: object @@ -459,11 +474,13 @@ components: description: The type of connector. enum: - .cases-webhook - example: .cases-webhook + examples: + - .cases-webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_cases_webhook' config_properties_d3security: @@ -505,11 +522,13 @@ components: description: The type of connector. enum: - .d3security - example: .d3security + examples: + - .d3security name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_d3security' config_properties_email: @@ -522,8 +541,9 @@ components: clientId: description: | The client identifier, which is a part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true + type: + - string + - 'null' from: description: | The from address for all emails sent by the connector. It must be specified in `user@host-name` format. @@ -538,8 +558,9 @@ components: The host name of the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. type: string oauthTokenUrl: - type: string - nullable: true + type: + - string + - 'null' port: description: | The port to connect to on the service provider. If the `service` is `elastic_cloud` (for Elastic Cloud notifications) or one of Nodemailer's well-known email service providers, this property is ignored. If `service` is `other`, this property must be defined. @@ -562,8 +583,9 @@ components: tenantId: description: | The tenant identifier, which is part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true + type: + - string + - 'null' secrets_properties_email: title: Connector secrets properties for an email connector description: Defines secrets for connectors when type is `.email`. @@ -599,11 +621,13 @@ components: description: The type of connector. enum: - .email - example: .email + examples: + - .email name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_email' config_properties_genai: @@ -667,11 +691,13 @@ components: description: The type of connector. enum: - .gen-ai - example: .gen-ai + examples: + - .gen-ai name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_genai' config_properties_index: @@ -684,8 +710,9 @@ components: executionTimeField: description: A field that indicates when the document was indexed. default: null - type: string - nullable: true + type: + - string + - 'null' index: description: The Elasticsearch index to be written to. type: string @@ -710,11 +737,13 @@ components: description: The type of connector. enum: - .index - example: .index + examples: + - .index name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector config_properties_jira: title: Connector request properties for a Jira connector required: @@ -760,11 +789,13 @@ components: description: The type of connector. enum: - .jira - example: .jira + examples: + - .jira name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_jira' config_properties_opsgenie: @@ -805,11 +836,13 @@ components: description: The type of connector. enum: - .opsgenie - example: .opsgenie + examples: + - .opsgenie name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_opsgenie' config_properties_pagerduty: @@ -819,9 +852,11 @@ components: properties: apiUrl: description: The PagerDuty event URL. - type: string - nullable: true - example: https://events.pagerduty.com/v2/enqueue + type: + - string + - 'null' + examples: + - https://events.pagerduty.com/v2/enqueue secrets_properties_pagerduty: title: Connector secrets properties for a PagerDuty connector description: Defines secrets for connectors when type is `.pagerduty`. @@ -851,11 +886,13 @@ components: description: The type of connector. enum: - .pagerduty - example: .pagerduty + examples: + - .pagerduty name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_pagerduty' config_properties_resilient: @@ -901,13 +938,15 @@ components: connector_type_id: description: The type of connector. type: string - example: .resilient + examples: + - .resilient enum: - .resilient name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_resilient' create_connector_request_serverlog: @@ -923,11 +962,13 @@ components: description: The type of connector. enum: - .server-log - example: .server-log + examples: + - .server-log name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector config_properties_servicenow: title: Connector request properties for a ServiceNow ITSM connector required: @@ -998,11 +1039,13 @@ components: description: The type of connector. enum: - .servicenow - example: .servicenow + examples: + - .servicenow name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' config_properties_servicenow_itom: @@ -1050,11 +1093,13 @@ components: description: The type of connector. enum: - .servicenow-itom - example: .servicenow-itom + examples: + - .servicenow-itom name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' create_connector_request_servicenow_sir: @@ -1075,11 +1120,13 @@ components: description: The type of connector. enum: - .servicenow-sir - example: .servicenow-sir + examples: + - .servicenow-sir name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_servicenow' secrets_properties_slack_api: @@ -1106,11 +1153,13 @@ components: description: The type of connector. enum: - .slack_api - example: .slack_api + examples: + - .slack_api name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_slack_api' secrets_properties_slack_webhook: @@ -1137,11 +1186,13 @@ components: description: The type of connector. enum: - .slack - example: .slack + examples: + - .slack name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_slack_webhook' config_properties_swimlane: @@ -1350,11 +1401,13 @@ components: description: The type of connector. enum: - .swimlane - example: .swimlane + examples: + - .swimlane name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_swimlane' secrets_properties_teams: @@ -1382,11 +1435,13 @@ components: description: The type of connector. enum: - .teams - example: .teams + examples: + - .teams name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_teams' config_properties_tines: @@ -1432,11 +1487,13 @@ components: description: The type of connector. enum: - .tines - example: .tines + examples: + - .tines name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_tines' config_properties_torq: @@ -1477,11 +1534,13 @@ components: description: The type of connector. enum: - .torq - example: .torq + examples: + - .torq name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_torq' config_properties_webhook: @@ -1513,8 +1572,9 @@ components: description: | If `true`, a user name and password must be provided for login type authentication. headers: - type: object - nullable: true + type: + - object + - 'null' description: A set of key-value pairs sent as headers with the request. method: type: string @@ -1577,11 +1637,13 @@ components: description: The type of connector. enum: - .webhook - example: .webhook + examples: + - .webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_webhook' config_properties_xmatters: @@ -1592,8 +1654,9 @@ components: configUrl: description: | The request URL for the Elastic Alerts trigger in xMatters. It is applicable only when `usesBasic` is `true`. - type: string - nullable: true + type: + - string + - 'null' usesBasic: description: Specifies whether the connector uses HTTP basic authentication (`true`) or URL authentication (`false`). type: boolean @@ -1633,11 +1696,13 @@ components: description: The type of connector. enum: - .xmatters - example: .xmatters + examples: + - .xmatters name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_xmatters' create_connector_request: @@ -1671,20 +1736,24 @@ components: is_deprecated: type: boolean description: Indicates whether the connector type is deprecated. - example: false + examples: + - false is_missing_secrets: type: boolean description: Indicates whether secrets are missing for the connector. Secrets configuration properties vary depending on the connector type. - example: false + examples: + - false is_preconfigured: type: boolean description: | Indicates whether it is a preconfigured connector. If true, the `config` and `is_missing_secrets` properties are omitted from the response. - example: false + examples: + - false is_system_action: type: boolean description: Indicates whether the connector is used for system actions. - example: false + examples: + - false connector_response_properties_bedrock: title: Connector response properties for an Amazon Bedrock connector type: object @@ -1721,7 +1790,8 @@ components: type: integer description: | Indicates the number of saved objects that reference the connector. If `is_preconfigured` is true, this value is not calculated. This property is returned only by the get all connectors API. - example: 2 + examples: + - 2 connector_response_properties_cases_webhook: title: Connector request properties for a Webhook - Case Management connector type: object @@ -1997,8 +2067,9 @@ components: - name properties: config: - type: object - nullable: true + type: + - object + - 'null' connector_type_id: type: string description: The type of connector. @@ -2432,7 +2503,8 @@ components: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_cases_webhook' update_connector_request_d3security: @@ -2612,7 +2684,8 @@ components: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: '#/components/schemas/secrets_properties_swimlane' update_connector_request_teams: @@ -2749,7 +2822,8 @@ components: - .torq - .webhook - .xmatters - example: .server-log + examples: + - .server-log examples: create_email_connector_request: summary: Create an email connector. @@ -2932,14 +3006,16 @@ components: properties: error: type: string - example: Unauthorized + examples: + - Unauthorized enum: - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 enum: - 401 '404': @@ -2952,14 +3028,17 @@ components: properties: error: type: string - example: Not Found + examples: + - Not Found enum: - Not Found message: type: string - example: Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found + examples: + - Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found statusCode: type: integer - example: 404 + examples: + - 404 enum: - 404 diff --git a/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_request.yaml b/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_request.yaml new file mode 100644 index 0000000000000..8124aac245ec7 --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_request.yaml @@ -0,0 +1,10 @@ +summary: Run a PagerDuty connector to trigger an alert. +value: + params: + eventAction: trigger + summary: A brief event summary + links: + - href: http://example.com/pagerduty + text: An example link + customDetails: + my_data_1: test data \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_response.yaml b/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_response.yaml new file mode 100644 index 0000000000000..81e941ce52a42 --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/examples/run_pagerduty_connector_response.yaml @@ -0,0 +1,8 @@ +summary: Response from running a PagerDuty connector. +value: + connector_id: 45de9f70-954f-4608-b12a-db7cf808e49d + data: + dedup_key: 5115e138b26b484a81eaea779faa6016 + message: Event processed + status: success + status: ok \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml b/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml index 3ee0b642c9dee..acbc6ab5acd63 100644 --- a/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the action. required: true schema: type: string - example: c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad + examples: + - c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad diff --git a/x-pack/plugins/actions/docs/openapi/components/parameters/connector_id.yaml b/x-pack/plugins/actions/docs/openapi/components/parameters/connector_id.yaml index d98c2cec803ff..fdf1487e626a8 100644 --- a/x-pack/plugins/actions/docs/openapi/components/parameters/connector_id.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/parameters/connector_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the connector. required: true schema: type: string - example: df770e30-8b8b-11ed-a780-3b746c987a81 + examples: + - df770e30-8b8b-11ed-a780-3b746c987a81 diff --git a/x-pack/plugins/actions/docs/openapi/components/parameters/space_id.yaml b/x-pack/plugins/actions/docs/openapi/components/parameters/space_id.yaml index 0a9fba457e3e7..45787e844caec 100644 --- a/x-pack/plugins/actions/docs/openapi/components/parameters/space_id.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/parameters/space_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the space. If `/s/` and the identifier are omitte required: true schema: type: string - example: default + examples: + - default diff --git a/x-pack/plugins/actions/docs/openapi/components/responses/400.yaml b/x-pack/plugins/actions/docs/openapi/components/responses/400.yaml index 263623dd1fb4c..4f8890737ff40 100644 --- a/x-pack/plugins/actions/docs/openapi/components/responses/400.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/responses/400.yaml @@ -6,10 +6,13 @@ content: properties: error: type: string - example: Bad Request + examples: + - Bad Request message: type: string - example: "error validating action type config: [index]: expected value of type [string] but got [undefined]" + examples: + - "error validating action type config: [index]: expected value of type [string] but got [undefined]" statusCode: type: integer - example: 400 \ No newline at end of file + examples: + - 400 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/responses/401.yaml b/x-pack/plugins/actions/docs/openapi/components/responses/401.yaml index ff5cbfd4c2768..78b77b3ab5f43 100644 --- a/x-pack/plugins/actions/docs/openapi/components/responses/401.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/responses/401.yaml @@ -7,13 +7,15 @@ content: properties: error: type: string - example: Unauthorized + examples: + - Unauthorized enum: - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 enum: - 401 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/responses/404.yaml b/x-pack/plugins/actions/docs/openapi/components/responses/404.yaml index 56964c6015c85..d4cf816f5903c 100644 --- a/x-pack/plugins/actions/docs/openapi/components/responses/404.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/responses/404.yaml @@ -7,14 +7,17 @@ content: properties: error: type: string - example: Not Found + examples: + - Not Found enum: - Not Found message: type: string - example: "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" + examples: + - "Saved object [action/baf33fc0-920c-11ed-b36a-874bd1548a00] not found" statusCode: type: integer - example: 404 + examples: + - 404 enum: - 404 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_cases_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_cases_webhook.yaml index 43945fbb241a2..81c94bd3e8806 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_cases_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_cases_webhook.yaml @@ -22,7 +22,8 @@ properties: connector. The JSON is validated once the Mustache variables have been placed when the REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: {"body": {{{case.comment}}}} + examples: + - {"body": {{{case.comment}}}} createCommentMethod: type: string description: > @@ -40,7 +41,8 @@ properties: You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts setting`, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}}/comment createIncidentJson: type: string description: > @@ -52,7 +54,8 @@ properties: connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: {"fields": {"summary": {{{case.title}}},"description": {{{case.description}}},"labels": {{{case.tags}}}}} + examples: + - {"fields": {"summary": {{{case.title}}},"description": {{{case.description}}},"labels": {{{case.tags}}}}} createIncidentMethod: type: string description: > @@ -86,7 +89,8 @@ properties: variables have been placed when REST method runs. Manually ensure that the JSON is valid, disregarding the Mustache variables, so the later validation will pass. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.id}}} hasAuth: type: boolean description: If true, a username and password for login type authentication must be provided. @@ -107,7 +111,8 @@ properties: connector. The JSON is validated after the Mustache variables have been placed when REST method runs. Manually ensure that the JSON is valid to avoid future validation errors; disregard Mustache variables during your review. - example: {"fields": {"summary": {{{case.title}}},"description": {{{case.description}}},"labels": {{{case.tags}}}}} + examples: + - {"fields": {"summary": {{{case.title}}},"description": {{{case.description}}},"labels": {{{case.tags}}}}} updateIncidentMethod: type: string description: > @@ -124,12 +129,14 @@ properties: The REST API URL to update the case by ID in the third-party system. You can use a variable to add the external system ID to the URL. If you are using the `xpack.actions.allowedHosts` setting, add the hostname to the allowed hosts. - example: https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} + examples: + - https://testing-jira.atlassian.net/rest/api/2/issue/{{{external.system.ID}}} viewIncidentUrl: type: string description: > The URL to view the case in the external system. You can use variables to add the external system ID or external system title to the URL. - example: https://testing-jira.atlassian.net/browse/{{{external.system.title}}} + examples: + - https://testing-jira.atlassian.net/browse/{{{external.system.title}}} diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_email.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_email.yaml index 6d3618e2bba27..202f4022a6fbd 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_email.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_email.yaml @@ -8,8 +8,9 @@ properties: description: > The client identifier, which is a part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true + type: + - "string" + - "null" from: description: > The from address for all emails sent by the connector. It must be specified in `user@host-name` format. @@ -27,8 +28,9 @@ properties: type: string oauthTokenUrl: # description: TBD - type: string - nullable: true + type: + - "string" + - "null" port: description: > The port to connect to on the service provider. @@ -55,5 +57,6 @@ properties: description: > The tenant identifier, which is part of OAuth 2.0 client credentials authentication, in GUID format. If `service` is `exchange_server`, this property is required. - type: string - nullable: true \ No newline at end of file + type: + - "string" + - "null" \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_index.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_index.yaml index 6c335b166d20a..f6d3af59b4937 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_index.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_index.yaml @@ -7,8 +7,9 @@ properties: executionTimeField: description: A field that indicates when the document was indexed. default: null - type: string - nullable: true + type: + - "string" + - "null" index: description: The Elasticsearch index to be written to. type: string diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_pagerduty.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_pagerduty.yaml index 562557f548ece..bfbec7b46190b 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_pagerduty.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_pagerduty.yaml @@ -4,6 +4,8 @@ type: object properties: apiUrl: description: The PagerDuty event URL. - type: string - nullable: true - example: https://events.pagerduty.com/v2/enqueue \ No newline at end of file + type: + - "string" + - "null" + examples: + - https://events.pagerduty.com/v2/enqueue \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_webhook.yaml index fe45366eca264..601d410666576 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_webhook.yaml @@ -27,8 +27,9 @@ properties: description: > If `true`, a user name and password must be provided for login type authentication. headers: - type: object - nullable: true + type: + - "object" + - "null" description: A set of key-value pairs sent as headers with the request. method: type: string diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_xmatters.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_xmatters.yaml index 350e96f3aa63d..3393c11ecd90d 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_xmatters.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_xmatters.yaml @@ -6,8 +6,9 @@ properties: description: > The request URL for the Elastic Alerts trigger in xMatters. It is applicable only when `usesBasic` is `true`. - type: string - nullable: true + type: + - "string" + - "null" usesBasic: description: Specifies whether the connector uses HTTP basic authentication (`true`) or URL authentication (`false`). type: boolean diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_response_properties_serverlog.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_response_properties_serverlog.yaml index a397e668102a6..da741478864b4 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_response_properties_serverlog.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_response_properties_serverlog.yaml @@ -8,8 +8,9 @@ required: - name properties: config: - type: object - nullable: true + type: + - "object" + - "null" connector_type_id: type: string description: The type of connector. diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml index 5d0bc42ab8317..dff10fcc778da 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml @@ -24,4 +24,5 @@ enum: - .torq - .webhook - .xmatters -example: .server-log \ No newline at end of file +examples: + - .server-log \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_bedrock.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_bedrock.yaml index 2acc21bfbfac7..e8feecb0051cd 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_bedrock.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_bedrock.yaml @@ -14,10 +14,12 @@ properties: description: The type of connector. enum: - .bedrock - example: .bedrock + examples: + - .bedrock name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_bedrock.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_cases_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_cases_webhook.yaml index bcbe840c03513..0cd030d740809 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_cases_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_cases_webhook.yaml @@ -15,10 +15,12 @@ properties: description: The type of connector. enum: - .cases-webhook - example: .cases-webhook + examples: + - .cases-webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_cases_webhook.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_d3security.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_d3security.yaml index 39cdda80b7dd2..6b5389cc80f31 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_d3security.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_d3security.yaml @@ -15,10 +15,12 @@ properties: description: The type of connector. enum: - .d3security - example: .d3security + examples: + - .d3security name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_d3security.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_email.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_email.yaml index 89f0b79c4e74b..1f1c6c079770a 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_email.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_email.yaml @@ -18,10 +18,12 @@ properties: description: The type of connector. enum: - .email - example: .email + examples: + - .email name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_email.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_genai.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_genai.yaml index 95d65bdb80919..725f842f91093 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_genai.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_genai.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .gen-ai - example: .gen-ai + examples: + - .gen-ai name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_genai.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_index.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_index.yaml index 26d6e118c1fe8..ad8e9be9a41dc 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_index.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_index.yaml @@ -13,8 +13,10 @@ properties: description: The type of connector. enum: - .index - example: .index + examples: + - .index name: type: string description: The display name for the connector. - example: my-connector \ No newline at end of file + examples: + - my-connector \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_jira.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_jira.yaml index 5b6077e875b24..95ccaa5b2ec6f 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_jira.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_jira.yaml @@ -14,10 +14,12 @@ properties: description: The type of connector. enum: - .jira - example: .jira + examples: + - .jira name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_jira.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_opsgenie.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_opsgenie.yaml index 6de1296dac43c..51c29f5cdd8fd 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_opsgenie.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_opsgenie.yaml @@ -14,10 +14,12 @@ properties: description: The type of connector. enum: - .opsgenie - example: .opsgenie + examples: + - .opsgenie name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_opsgenie.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_pagerduty.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_pagerduty.yaml index 498488299afd3..66ffc61d30f30 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_pagerduty.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_pagerduty.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .pagerduty - example: .pagerduty + examples: + - .pagerduty name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_pagerduty.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_resilient.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_resilient.yaml index c3f766625b7da..60467336c0d9a 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_resilient.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_resilient.yaml @@ -12,12 +12,14 @@ properties: connector_type_id: description: The type of connector. type: string - example: .resilient + examples: + - .resilient enum: - .resilient name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_resilient.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_serverlog.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_serverlog.yaml index eac0a0d65b69f..0cb85403663c6 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_serverlog.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_serverlog.yaml @@ -10,8 +10,10 @@ properties: description: The type of connector. enum: - .server-log - example: .server-log + examples: + - .server-log name: type: string description: The display name for the connector. - example: my-connector \ No newline at end of file + examples: + - my-connector \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow.yaml index e03303dcada4f..b0f35483cc39f 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .servicenow - example: .servicenow + examples: + - .servicenow name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_servicenow.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_itom.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_itom.yaml index 70a4c05c96522..bfbeb231ca7dc 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_itom.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_itom.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .servicenow-itom - example: .servicenow-itom + examples: + - .servicenow-itom name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_servicenow.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_sir.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_sir.yaml index 4d247c456f3e6..37519eb63f27b 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_sir.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_servicenow_sir.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .servicenow-sir - example: .servicenow-sir + examples: + - .servicenow-sir name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_servicenow.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_api.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_api.yaml index a16ba7416e7bc..07dbadbef9891 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_api.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_api.yaml @@ -11,10 +11,12 @@ properties: description: The type of connector. enum: - .slack_api - example: .slack_api + examples: + - .slack_api name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_slack_api.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_webhook.yaml index 1c046cc3f000c..3e884daa6e3b8 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_slack_webhook.yaml @@ -11,10 +11,12 @@ properties: description: The type of connector. enum: - .slack - example: .slack + examples: + - .slack name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_slack_webhook.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_swimlane.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_swimlane.yaml index 3de4f5ecbccef..633438a721ee9 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_swimlane.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_swimlane.yaml @@ -14,10 +14,12 @@ properties: description: The type of connector. enum: - .swimlane - example: .swimlane + examples: + - .swimlane name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_swimlane.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_teams.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_teams.yaml index 5e0d449bf5546..787f057c09ce6 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_teams.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_teams.yaml @@ -11,10 +11,12 @@ properties: description: The type of connector. enum: - .teams - example: .teams + examples: + - .teams name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_teams.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_tines.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_tines.yaml index 224c3e03c4363..c5333c8acc479 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_tines.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_tines.yaml @@ -15,10 +15,12 @@ properties: description: The type of connector. enum: - .tines - example: .tines + examples: + - .tines name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_tines.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_torq.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_torq.yaml index 934f9c9c1b395..a4ab3cc92aa0d 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_torq.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_torq.yaml @@ -15,10 +15,12 @@ properties: description: The type of connector. enum: - .torq - example: .torq + examples: + - .torq name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_torq.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_webhook.yaml index e0ead115d48dc..30e9663da8d99 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_webhook.yaml @@ -15,10 +15,12 @@ properties: description: The type of connector. enum: - .webhook - example: .webhook + examples: + - .webhook name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_webhook.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_xmatters.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_xmatters.yaml index 13213d39561b2..753888b16ae5e 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_xmatters.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/create_connector_request_xmatters.yaml @@ -16,10 +16,12 @@ properties: description: The type of connector. enum: - .xmatters - example: .xmatters + examples: + - .xmatters name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_xmatters.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/is_deprecated.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/is_deprecated.yaml index 75fb1efe2f589..ac0a26102eed1 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/is_deprecated.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/is_deprecated.yaml @@ -1,3 +1,4 @@ type: boolean description: Indicates whether the connector type is deprecated. -example: false \ No newline at end of file +examples: + - false \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/is_missing_secrets.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/is_missing_secrets.yaml index cad03a44f8629..a7ad3f9542b3f 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/is_missing_secrets.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/is_missing_secrets.yaml @@ -1,3 +1,4 @@ type: boolean description: Indicates whether secrets are missing for the connector. Secrets configuration properties vary depending on the connector type. -example: false \ No newline at end of file +examples: + - false \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/is_preconfigured.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/is_preconfigured.yaml index e38741c83718e..d3f711c229399 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/is_preconfigured.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/is_preconfigured.yaml @@ -2,4 +2,5 @@ type: boolean description: > Indicates whether it is a preconfigured connector. If true, the `config` and `is_missing_secrets` properties are omitted from the response. -example: false \ No newline at end of file +examples: + - false \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/is_system_action.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/is_system_action.yaml index fd0dd06ef5fff..5a78f4676646f 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/is_system_action.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/is_system_action.yaml @@ -1,3 +1,4 @@ type: boolean description: Indicates whether the connector is used for system actions. -example: false \ No newline at end of file +examples: + - false \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/referenced_by_count.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/referenced_by_count.yaml index 61579fa3dc6ce..0a65bf9a854ff 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/referenced_by_count.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/referenced_by_count.yaml @@ -3,4 +3,5 @@ description: > Indicates the number of saved objects that reference the connector. If `is_preconfigured` is true, this value is not calculated. This property is returned only by the get all connectors API. -example: 2 \ No newline at end of file +examples: + - 2 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_acknowledge_resolve_pagerduty.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_acknowledge_resolve_pagerduty.yaml new file mode 100644 index 0000000000000..d5e9087cdb7ba --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_acknowledge_resolve_pagerduty.yaml @@ -0,0 +1,17 @@ +title: PagerDuty connector parameters +description: Test an action that acknowledges or resolves a PagerDuty alert. +type: object +required: + - dedupKey + - eventAction +properties: + dedupKey: + description: The deduplication key for the PagerDuty alert. + type: string + maxLength: 255 + eventAction: + description: The type of event. + type: string + enum: + - acknowledge + - resolve \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_trigger_pagerduty.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_trigger_pagerduty.yaml new file mode 100644 index 0000000000000..75a59af156264 --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_params_trigger_pagerduty.yaml @@ -0,0 +1,69 @@ +title: PagerDuty connector parameters +description: Test an action that triggers a PagerDuty alert. +type: object +required: + - eventAction +properties: + class: + description: The class or type of the event. + type: string + examples: + - cpu load + component: + description: The component of the source machine that is responsible for the event. + type: string + examples: + - eth0 + customDetails: + description: Additional details to add to the event. + type: object + dedupKey: + description: > + All actions sharing this key will be associated with the same PagerDuty alert. + This value is used to correlate trigger and resolution. + type: string + maxLength: 255 + eventAction: + description: The type of event. + type: string + enum: + - trigger + group: + description: The logical grouping of components of a service. + type: string + examples: + - app-stack + links: + description: A list of links to add to the event. + type: array + items: + type: object + properties: + href: + description: The URL for the link. + type: string + text: + description: A plain text description of the purpose of the link. + type: string + severity: + description: The severity of the event on the affected system. + type: string + enum: + - critical + - error + - info + - warning + default: info + source: + description: > + The affected system, such as a hostname or fully qualified domain name. + Defaults to the Kibana saved object id of the action. + type: string + summary: + description: A summery of the event. + type: string + maxLength: 1024 + timestamp: + description: An ISO-8601 timestamp that indicates when the event was detected or generated. + type: string + format: date-time \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_request.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_request.yaml index 7602693cebe50..13b0623a01ab8 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_request.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_request.yaml @@ -6,9 +6,11 @@ required: properties: params: oneOf: + - $ref: 'run_connector_params_acknowledge_resolve_pagerduty.yaml' - $ref: 'run_connector_params_documents.yaml' - $ref: 'run_connector_params_message_email.yaml' - $ref: 'run_connector_params_message_serverlog.yaml' + - $ref: 'run_connector_params_trigger_pagerduty.yaml' - title: Subaction parameters description: Test an action that involves a subaction. oneOf: diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_createalert.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_createalert.yaml index e739a9ed6c91d..a53560951361f 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_createalert.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_createalert.yaml @@ -30,7 +30,8 @@ properties: type: object description: The custom properties of the alert. additionalProperties: true - example: {"key1":"value1","key2":"value2"} + examples: + - {"key1":"value1","key2":"value2"} entity: type: string description: The domain of the alert. For example, the application or server name. diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_fieldsbyissuetype.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_fieldsbyissuetype.yaml index e8c8869e7d68b..6c39957c29fc4 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_fieldsbyissuetype.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_fieldsbyissuetype.yaml @@ -18,5 +18,6 @@ properties: id: type: string description: The Jira issue type identifier. - example: 10024 + examples: + - 10024 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_getincident.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_getincident.yaml index 666c0257f68b8..7a16f3d9f8295 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_getincident.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_getincident.yaml @@ -18,4 +18,5 @@ properties: externalId: type: string description: The Jira, ServiceNow ITSM, or ServiceNow SecOps issue identifier. - example: 71778 + examples: + - 71778 diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_issue.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_issue.yaml index 56ee923b40063..3743e7fa90bd3 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_issue.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/run_connector_subaction_issue.yaml @@ -17,4 +17,5 @@ properties: id: type: string description: The Jira issue identifier. - example: 71778 \ No newline at end of file + examples: + - 71778 \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_cases_webhook.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_cases_webhook.yaml index 66250b31a94eb..9201a1b1e1d70 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_cases_webhook.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_cases_webhook.yaml @@ -9,6 +9,7 @@ properties: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_cases_webhook.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_swimlane.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_swimlane.yaml index 81321351b74ec..771625841a042 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_swimlane.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/update_connector_request_swimlane.yaml @@ -10,6 +10,7 @@ properties: name: type: string description: The display name for the connector. - example: my-connector + examples: + - my-connector secrets: $ref: 'secrets_properties_swimlane.yaml' \ No newline at end of file diff --git a/x-pack/plugins/actions/docs/openapi/entrypoint.yaml b/x-pack/plugins/actions/docs/openapi/entrypoint.yaml index bdbf408de3e1a..d082d91a2a4e5 100644 --- a/x-pack/plugins/actions/docs/openapi/entrypoint.yaml +++ b/x-pack/plugins/actions/docs/openapi/entrypoint.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Connectors description: OpenAPI schema for Connectors endpoints - version: '0.1' + version: '0.2' contact: name: Connectors Team license: @@ -51,7 +51,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - apiKeyAuth: [] diff --git a/x-pack/plugins/actions/docs/openapi/entrypoint_serverless.yaml b/x-pack/plugins/actions/docs/openapi/entrypoint_serverless.yaml index 3503221c4e306..4780a65da6520 100644 --- a/x-pack/plugins/actions/docs/openapi/entrypoint_serverless.yaml +++ b/x-pack/plugins/actions/docs/openapi/entrypoint_serverless.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Connectors description: OpenAPI schema for connectors in Serverless projects - version: '0.1' + version: '0.2' contact: name: Connectors Team license: @@ -30,6 +30,7 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - apiKeyAuth: [] diff --git a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}.yaml b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}.yaml index e4b8e1c468643..6464b9592436a 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}.yaml @@ -51,7 +51,8 @@ post: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: diff --git a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}@_execute.yaml b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}@_execute.yaml index 577a57fbb5d96..a276f30292daf 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}@_execute.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector@{connectorid}@_execute.yaml @@ -23,6 +23,8 @@ post: $ref: '../components/examples/run_index_connector_request.yaml' runJiraConnectorRequest: $ref: '../components/examples/run_jira_connector_request.yaml' + runPagerDutyConnectorRequest: + $ref: '../components/examples/run_pagerduty_connector_request.yaml' runServerLogConnectorRequest: $ref: '../components/examples/run_server_log_connector_request.yaml' runServiceNowITOMConnectorRequest: @@ -65,6 +67,8 @@ post: $ref: '../components/examples/run_index_connector_response.yaml' runJiraConnectorResponse: $ref: '../components/examples/run_jira_connector_response.yaml' + runPagerDutyConnectorResponse: + $ref: '../components/examples/run_pagerduty_connector_response.yaml' runServerLogConnectorResponse: $ref: '../components/examples/run_server_log_connector_response.yaml' runServiceNowITOMConnectorResponse: diff --git a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector_types.yaml b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector_types.yaml index 530fed05e3046..94dbb727eea4a 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector_types.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/api@actions@connector_types.yaml @@ -24,37 +24,41 @@ get: enabled: type: boolean description: Indicates whether the connector type is enabled in Kibana. - example: true + examples: + - true enabled_in_config: type: boolean description: Indicates whether the connector type is enabled in the Kibana configuration file. - example: true + examples: + - true enabled_in_license: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: $ref: '../components/schemas/connector_types.yaml' is_system_action_type: type: boolean - example: false + examples: + - false minimum_license_required: type: string description: The license that is required to use the connector type. - example: basic + examples: + - basic name: type: string description: The name of the connector type. - example: Index + examples: + - Index supported_feature_ids: type: array description: The features that are supported by the connector type. items: $ref: '../components/schemas/features.yaml' - example: - - alerting - - cases - - siem + examples: + - [alerting, cases, siem] examples: getConnectorTypesServerlessResponse: $ref: '../components/examples/get_connector_types_generativeai_response.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector@{connectorid}.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector@{connectorid}.yaml index 1d014404a643a..27351f0954eee 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector@{connectorid}.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector@{connectorid}.yaml @@ -59,7 +59,8 @@ post: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector_types.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector_types.yaml index 86ef1cd5460f1..9a0fababdf166 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector_types.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@connector_types.yaml @@ -27,34 +27,37 @@ get: enabled: type: boolean description: Indicates whether the connector type is enabled in Kibana. - example: true + examples: + - true enabled_in_config: type: boolean description: Indicates whether the connector type is enabled in the Kibana `.yml` file. - example: true + examples: + - true enabled_in_license: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: $ref: '../components/schemas/connector_types.yaml' minimum_license_required: type: string description: The license that is required to use the connector type. - example: basic + examples: + - basic name: type: string description: The name of the connector type. - example: Index + examples: + - Index supported_feature_ids: type: array description: The Kibana features that are supported by the connector type. items: $ref: '../components/schemas/features.yaml' - example: - - alerting - - uptime - - siem + examples: + - [alerting, uptime, siem] examples: getConnectorTypesResponse: $ref: '../components/examples/get_connector_types_response.yaml' diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml index edda7d4aca310..6bc2b9e5e53a8 100644 --- a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml +++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml @@ -28,7 +28,8 @@ get: enabledInLicense: type: boolean description: Indicates whether the connector is enabled in the license. - example: true + examples: + - true id: type: string description: The unique identifier for the connector type. 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 9b2fd5e987f6c..9e277b9084329 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -22,6 +22,7 @@ import { import { securityMock } from '@kbn/security-plugin/server/mocks'; import { finished } from 'stream/promises'; import { PassThrough } from 'stream'; +import { SecurityConnectorFeatureId } from '../../common'; const actionExecutor = new ActionExecutor({ isESOCanEncrypt: true }); const services = actionsMock.createServices(); @@ -839,6 +840,44 @@ test('successfully authorize system actions', async () => { }); }); +test('Execute of SentinelOne sub-actions require create privilege', async () => { + const actionType: jest.Mocked = { + id: '.sentinelone', + name: 'sentinelone', + minimumLicenseRequired: 'enterprise', + supportedFeatureIds: [SecurityConnectorFeatureId], + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + const actionSavedObject = { + id: '1', + type: 'action', + attributes: { + name: '1', + actionTypeId: '.sentinelone', + config: { + bar: true, + }, + secrets: { + baz: true, + }, + isMissingSecrets: false, + }, + references: [], + }; + + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); + actionTypeRegistry.get.mockReturnValueOnce(actionType); + + await actionExecutor.execute({ ...executeParams, actionId: 'sentinel-one-connector-authz' }); + + expect(authorizationMock.ensureAuthorized).toHaveBeenCalledWith({ operation: 'create' }); +}); + test('pass the params to the actionTypeRegistry when authorizing system actions', async () => { const actionType: jest.Mocked = { id: '.cases', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index b27e2c1ee79c7..fd1d8f92a0482 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -567,6 +567,14 @@ const ensureAuthorizedToExecute = async ({ await authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } + + // SentinelOne sub-actions require that a user have `all` privilege to Actions and Connectors. + // This is a temporary solution until a more robust RBAC approach can be implemented for sub-actions + if (actionTypeId === '.sentinelone') { + await authorization.ensureAuthorized({ + operation: 'create', + }); + } } catch (error) { throw new ActionExecutionError(error.message, ActionExecutionErrorReason.Authorization, { actionId, diff --git a/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts b/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts index 7c104177ea36e..866580e8e7b3b 100644 --- a/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts +++ b/x-pack/plugins/actions/server/lib/gen_ai_token_tracking.ts @@ -42,6 +42,7 @@ export const getGenAiTokenTracking = async ({ try { const { total, prompt, completion } = await getTokenCountFromInvokeStream({ responseStream: result.data.pipe(new PassThrough()), + actionTypeId, body: (validatedParams as { subActionParams: InvokeBody }).subActionParams, logger, }); diff --git a/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.test.ts b/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.test.ts index 3c0dd66130f3a..2d8f86b881728 100644 --- a/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.test.ts +++ b/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.test.ts @@ -7,20 +7,15 @@ import { Transform } from 'stream'; import { getTokenCountFromInvokeStream } from './get_token_count_from_invoke_stream'; import { loggerMock } from '@kbn/logging-mocks'; +import { EventStreamCodec } from '@smithy/eventstream-codec'; +import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; -interface StreamMock { - write: (data: string) => void; - fail: () => void; - complete: () => void; - transform: Transform; -} - -function createStreamMock(): StreamMock { +function createStreamMock() { const transform: Transform = new Transform({}); return { - write: (data: string) => { - transform.push(`${data}\n`); + write: (data: unknown) => { + transform.push(data); }, fail: () => { transform.emit('error', new Error('Stream failed')); @@ -34,7 +29,10 @@ function createStreamMock(): StreamMock { } const logger = loggerMock.create(); describe('getTokenCountFromInvokeStream', () => { - let stream: StreamMock; + beforeEach(() => { + jest.resetAllMocks(); + }); + let stream: ReturnType; const body = { messages: [ { @@ -48,36 +46,79 @@ describe('getTokenCountFromInvokeStream', () => { ], }; + const chunk = { + object: 'chat.completion.chunk', + choices: [ + { + delta: { + content: 'Single.', + }, + }, + ], + }; + const PROMPT_TOKEN_COUNT = 34; const COMPLETION_TOKEN_COUNT = 2; + describe('OpenAI stream', () => { + beforeEach(() => { + stream = createStreamMock(); + stream.write(`data: ${JSON.stringify(chunk)}`); + }); - beforeEach(() => { - stream = createStreamMock(); - stream.write('Single'); - }); - - describe('when a stream completes', () => { - beforeEach(async () => { + it('counts the prompt + completion tokens for OpenAI response', async () => { stream.complete(); - }); - it('counts the prompt tokens', async () => { const tokens = await getTokenCountFromInvokeStream({ responseStream: stream.transform, body, logger, + actionTypeId: '.gen-ai', }); expect(tokens.prompt).toBe(PROMPT_TOKEN_COUNT); expect(tokens.completion).toBe(COMPLETION_TOKEN_COUNT); expect(tokens.total).toBe(PROMPT_TOKEN_COUNT + COMPLETION_TOKEN_COUNT); }); + it('resolves the promise with the correct prompt tokens', async () => { + const tokenPromise = getTokenCountFromInvokeStream({ + responseStream: stream.transform, + body, + logger, + actionTypeId: '.gen-ai', + }); + + stream.fail(); + + await expect(tokenPromise).resolves.toEqual({ + prompt: PROMPT_TOKEN_COUNT, + total: PROMPT_TOKEN_COUNT + COMPLETION_TOKEN_COUNT, + completion: COMPLETION_TOKEN_COUNT, + }); + expect(logger.error).toHaveBeenCalled(); + }); }); + describe('Bedrock stream', () => { + beforeEach(() => { + stream = createStreamMock(); + stream.write(encodeBedrockResponse('Simple.')); + }); - describe('when a stream fails', () => { + it('counts the prompt + completion tokens for OpenAI response', async () => { + stream.complete(); + const tokens = await getTokenCountFromInvokeStream({ + responseStream: stream.transform, + body, + logger, + actionTypeId: '.bedrock', + }); + expect(tokens.prompt).toBe(PROMPT_TOKEN_COUNT); + expect(tokens.completion).toBe(COMPLETION_TOKEN_COUNT); + expect(tokens.total).toBe(PROMPT_TOKEN_COUNT + COMPLETION_TOKEN_COUNT); + }); it('resolves the promise with the correct prompt tokens', async () => { const tokenPromise = getTokenCountFromInvokeStream({ responseStream: stream.transform, body, logger, + actionTypeId: '.bedrock', }); stream.fail(); @@ -91,3 +132,16 @@ describe('getTokenCountFromInvokeStream', () => { }); }); }); + +function encodeBedrockResponse(completion: string) { + return new EventStreamCodec(toUtf8, fromUtf8).encode({ + headers: {}, + body: Uint8Array.from( + Buffer.from( + JSON.stringify({ + bytes: Buffer.from(JSON.stringify({ completion })).toString('base64'), + }) + ) + ), + }); +} diff --git a/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.ts b/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.ts index 594fec89d93c0..dfb4bae69f8cf 100644 --- a/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.ts +++ b/x-pack/plugins/actions/server/lib/get_token_count_from_invoke_stream.ts @@ -9,6 +9,8 @@ import { Logger } from '@kbn/logging'; import { encode } from 'gpt-tokenizer'; import { Readable } from 'stream'; import { finished } from 'stream/promises'; +import { EventStreamCodec } from '@smithy/eventstream-codec'; +import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; export interface InvokeBody { messages: Array<{ @@ -26,10 +28,12 @@ export interface InvokeBody { * @param logger the logger */ export async function getTokenCountFromInvokeStream({ + actionTypeId, responseStream, body, logger, }: { + actionTypeId: string; responseStream: Readable; body: InvokeBody; logger: Logger; @@ -47,9 +51,37 @@ export async function getTokenCountFromInvokeStream({ .join('\n') ).length; - let responseBody: string = ''; + const parser = actionTypeId === '.bedrock' ? parseBedrockStream : parseOpenAIStream; + const parsedResponse = await parser(responseStream, logger); + + const completionTokens = encode(parsedResponse).length; + return { + prompt: promptTokens, + completion: completionTokens, + total: promptTokens + completionTokens, + }; +} + +type StreamParser = (responseStream: Readable, logger: Logger) => Promise; - responseStream.on('data', (chunk: string) => { +const parseBedrockStream: StreamParser = async (responseStream, logger) => { + const responseBuffer: Uint8Array[] = []; + responseStream.on('data', (chunk) => { + // special encoding for bedrock, do not attempt to convert to string + responseBuffer.push(chunk); + }); + try { + await finished(responseStream); + } catch (e) { + logger.error('An error occurred while calculating streaming response tokens'); + } + return parseBedrockBuffer(responseBuffer); +}; + +const parseOpenAIStream: StreamParser = async (responseStream, logger) => { + let responseBody: string = ''; + responseStream.on('data', (chunk) => { + // no special encoding, can safely use toString and append to responseBody responseBody += chunk.toString(); }); try { @@ -57,12 +89,109 @@ export async function getTokenCountFromInvokeStream({ } catch (e) { logger.error('An error occurred while calculating streaming response tokens'); } + return parseOpenAIResponse(responseBody); +}; - const completionTokens = encode(responseBody).length; +/** + * Parses a Bedrock buffer from an array of chunks. + * + * @param {Uint8Array[]} chunks - Array of Uint8Array chunks to be parsed. + * @returns {string} - Parsed string from the Bedrock buffer. + */ +const parseBedrockBuffer = (chunks: Uint8Array[]): string => { + // Initialize an empty Uint8Array to store the concatenated buffer. + let bedrockBuffer: Uint8Array = new Uint8Array(0); - return { - prompt: promptTokens, - completion: completionTokens, - total: promptTokens + completionTokens, - }; + // Map through each chunk to process the Bedrock buffer. + return chunks + .map((chunk) => { + // Concatenate the current chunk to the existing buffer. + bedrockBuffer = concatChunks(bedrockBuffer, chunk); + // Get the length of the next message in the buffer. + let messageLength = getMessageLength(bedrockBuffer); + // Initialize an array to store fully formed message chunks. + const buildChunks = []; + // Process the buffer until no complete messages are left. + while (bedrockBuffer.byteLength > 0 && bedrockBuffer.byteLength >= messageLength) { + // Extract a chunk of the specified length from the buffer. + const extractedChunk = bedrockBuffer.slice(0, messageLength); + // Add the extracted chunk to the array of fully formed message chunks. + buildChunks.push(extractedChunk); + // Remove the processed chunk from the buffer. + bedrockBuffer = bedrockBuffer.slice(messageLength); + // Get the length of the next message in the updated buffer. + messageLength = getMessageLength(bedrockBuffer); + } + + const awsDecoder = new EventStreamCodec(toUtf8, fromUtf8); + + // Decode and parse each message chunk, extracting the 'completion' property. + return buildChunks + .map((bChunk) => { + const event = awsDecoder.decode(bChunk); + const body = JSON.parse( + Buffer.from(JSON.parse(new TextDecoder().decode(event.body)).bytes, 'base64').toString() + ); + return body.completion; + }) + .join(''); + }) + .join(''); +}; + +/** + * Concatenates two Uint8Array buffers. + * + * @param {Uint8Array} a - First buffer. + * @param {Uint8Array} b - Second buffer. + * @returns {Uint8Array} - Concatenated buffer. + */ +function concatChunks(a: Uint8Array, b: Uint8Array): Uint8Array { + const newBuffer = new Uint8Array(a.length + b.length); + // Copy the contents of the first buffer to the new buffer. + newBuffer.set(a); + // Copy the contents of the second buffer to the new buffer starting from the end of the first buffer. + newBuffer.set(b, a.length); + return newBuffer; +} + +/** + * Gets the length of the next message from the buffer. + * + * @param {Uint8Array} buffer - Buffer containing the message. + * @returns {number} - Length of the next message. + */ +function getMessageLength(buffer: Uint8Array): number { + // If the buffer is empty, return 0. + if (buffer.byteLength === 0) return 0; + // Create a DataView to read the Uint32 value at the beginning of the buffer. + const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + // Read and return the Uint32 value (message length). + return view.getUint32(0, false); } + +const parseOpenAIResponse = (responseBody: string) => + responseBody + .split('\n') + .filter((line) => { + return line.startsWith('data: ') && !line.endsWith('[DONE]'); + }) + .map((line) => { + return JSON.parse(line.replace('data: ', '')); + }) + .filter( + ( + line + ): line is { + choices: Array<{ + delta: { content?: string; function_call?: { name?: string; arguments: string } }; + }>; + } => { + return 'object' in line && line.object === 'chat.completion.chunk'; + } + ) + .reduce((prev, line) => { + const msg = line.choices[0].delta!; + prev += msg.content || ''; + return prev; + }, ''); diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json index 3beeddef429b2..ea8096271d691 100644 --- a/x-pack/plugins/actions/tsconfig.json +++ b/x-pack/plugins/actions/tsconfig.json @@ -43,7 +43,7 @@ "@kbn/core-saved-objects-api-server-mocks", "@kbn/core-elasticsearch-server-mocks", "@kbn/core-logging-server-mocks", - "@kbn/serverless" + "@kbn/serverless", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms.ts b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms.ts new file mode 100644 index 0000000000000..c597a18e2f050 --- /dev/null +++ b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms.ts @@ -0,0 +1,142 @@ +/* + * Copyright 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 { SignificantItem } from '@kbn/ml-agg-utils'; + +// Named topTerms since all these items are of type `keyword`. +export const topTerms: SignificantItem[] = [ + { + bg_count: 0, + doc_count: 5102, + fieldName: 'version', + fieldValue: 'v1.0.0', + key: 'version:v1.0.0', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 2272, + fieldName: 'response_code', + fieldValue: '500', + key: 'response_code:500', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 2197, + fieldName: 'url', + fieldValue: 'home.php', + key: 'url:home.php', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1981, + fieldName: 'user', + fieldValue: 'Peter', + key: 'user:Peter', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1773, + fieldName: 'user', + fieldValue: 'Paul', + key: 'user:Paul', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1574, + fieldName: 'url', + fieldValue: 'login.php', + key: 'url:login.php', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1569, + fieldName: 'response_code', + fieldValue: '404', + key: 'response_code:404', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1348, + fieldName: 'user', + fieldValue: 'Mary', + key: 'user:Mary', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1331, + fieldName: 'url', + fieldValue: 'user.php', + key: 'url:user.php', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, + { + bg_count: 0, + doc_count: 1261, + fieldName: 'response_code', + fieldValue: '200', + key: 'response_code:200', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', + }, +]; diff --git a/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms_groups.ts b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms_groups.ts new file mode 100644 index 0000000000000..582fded59d7bb --- /dev/null +++ b/x-pack/plugins/aiops/common/__mocks__/artificial_logs/top_terms_groups.ts @@ -0,0 +1,1086 @@ +/* + * Copyright 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 { SignificantItemGroup } from '@kbn/ml-agg-utils'; + +export const topTermsGroups: SignificantItemGroup[] = [ + { + id: '1921491265', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 1097, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 602, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 602, + pValue: 1, + }, + { + id: '1354434627', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 1097, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 495, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 495, + pValue: 1, + }, + { + id: '1539462188', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 787, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 411, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 411, + pValue: 1, + }, + { + id: '1553868016', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 787, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 376, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 376, + pValue: 1, + }, + { + id: '3400778144', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 473, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 318, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 318, + pValue: 1, + }, + { + id: '2753923470', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 627, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 318, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 318, + pValue: 1, + }, + { + id: '611881711', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 420, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 317, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 317, + pValue: 1, + }, + { + id: '1605132418', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 523, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 317, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 317, + pValue: 1, + }, + { + id: '1389551523', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 368, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 316, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 316, + pValue: 1, + }, + { + id: '1156337696', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 419, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 316, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 316, + pValue: 1, + }, + { + id: '3582406482', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 627, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 190, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 190, + pValue: 1, + }, + { + id: '1758796965', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 388, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 190, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 190, + pValue: 1, + }, + { + id: '943892302', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 523, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 127, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 127, + pValue: 1, + }, + { + id: '3952579328', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 627, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 119, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 119, + pValue: 1, + }, + { + id: '1573710542', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 388, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 119, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 119, + pValue: 1, + }, + { + id: '1384949010', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 473, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 95, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 95, + pValue: 1, + }, + { + id: '3945828984', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 523, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 79, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 79, + pValue: 1, + }, + { + id: '3487185726', + group: [ + { + key: 'response_code:500', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '500', + docCount: 2272, + pValue: 1, + duplicate: 7, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 2272, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 388, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 79, + pValue: 1, + duplicate: 7, + }, + ], + docCount: 79, + pValue: 1, + }, + { + id: '113295987', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 420, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 63, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 63, + pValue: 1, + }, + { + id: '2896531546', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 419, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 63, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 63, + pValue: 1, + }, + { + id: '2376883532', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:home.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'home.php', + docCount: 473, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 60, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 60, + pValue: 1, + }, + { + id: '3927320460', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:user.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'user.php', + docCount: 420, + pValue: 1, + duplicate: 9, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 40, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 40, + pValue: 1, + }, + { + id: '769745306', + group: [ + { + key: 'response_code:404', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '404', + docCount: 1569, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1569, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 419, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 40, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 40, + pValue: 1, + }, + { + id: '2532116164', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 368, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Paul', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Paul', + docCount: 32, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 32, + pValue: 1, + }, + { + id: '1953946181', + group: [ + { + key: 'response_code:200', + type: 'keyword', + fieldName: 'response_code', + fieldValue: '200', + docCount: 1261, + pValue: 1, + duplicate: 9, + }, + { + key: 'version:v1.0.0', + type: 'keyword', + fieldName: 'version', + fieldValue: 'v1.0.0', + docCount: 1261, + pValue: 1, + duplicate: 25, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 368, + pValue: 1, + duplicate: 8, + }, + { + key: 'user:Mary', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Mary', + docCount: 20, + pValue: 1, + duplicate: 9, + }, + ], + docCount: 20, + pValue: 1, + }, +]; diff --git a/x-pack/plugins/aiops/common/api/log_rate_analysis/actions.ts b/x-pack/plugins/aiops/common/api/log_rate_analysis/actions.ts index bd3afd3152ae5..2348b32c32f8d 100644 --- a/x-pack/plugins/aiops/common/api/log_rate_analysis/actions.ts +++ b/x-pack/plugins/aiops/common/api/log_rate_analysis/actions.ts @@ -36,6 +36,7 @@ export const API_ACTION_NAME = { RESET_ALL: 'reset_all', RESET_ERRORS: 'reset_errors', RESET_GROUPS: 'reset_groups', + SET_ZERO_DOCS_FALLBACK: 'set_zero_docs_fallback', UPDATE_LOADING_STATE: 'update_loading_state', } as const; export type ApiActionName = typeof API_ACTION_NAME[keyof typeof API_ACTION_NAME]; @@ -210,6 +211,20 @@ export function updateLoadingStateAction( }; } +interface ApiActionSetZeroDocsFallback { + type: typeof API_ACTION_NAME.SET_ZERO_DOCS_FALLBACK; + payload: boolean; +} + +export function setZeroDocsFallback( + payload: ApiActionSetZeroDocsFallback['payload'] +): ApiActionSetZeroDocsFallback { + return { + type: API_ACTION_NAME.SET_ZERO_DOCS_FALLBACK, + payload, + }; +} + export type AiopsLogRateAnalysisApiAction = | ApiActionAddSignificantItems | ApiActionAddSignificantItemsGroup @@ -220,4 +235,5 @@ export type AiopsLogRateAnalysisApiAction = | ApiActionResetAll | ApiActionResetErrors | ApiActionResetGroups - | ApiActionUpdateLoadingState; + | ApiActionUpdateLoadingState + | ApiActionSetZeroDocsFallback; diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts index f3dd6cce856c7..5344d2448463c 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.test.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.test.ts @@ -31,6 +31,7 @@ describe('streamReducer', () => { significantItems: [], significantItemsGroups: [], errors: [], + zeroDocsFallback: false, }); }); diff --git a/x-pack/plugins/aiops/common/api/stream_reducer.ts b/x-pack/plugins/aiops/common/api/stream_reducer.ts index 05d3fce52c22e..06da4d3d250ad 100644 --- a/x-pack/plugins/aiops/common/api/stream_reducer.ts +++ b/x-pack/plugins/aiops/common/api/stream_reducer.ts @@ -18,6 +18,7 @@ interface StreamState { loadingState: string; remainingFieldCandidates?: string[]; groupsMissing?: boolean; + zeroDocsFallback: boolean; } export const initialState: StreamState = { @@ -27,6 +28,7 @@ export const initialState: StreamState = { errors: [], loaded: 0, loadingState: '', + zeroDocsFallback: false, }; export function streamReducer( @@ -72,6 +74,8 @@ export function streamReducer( return initialState; case API_ACTION_NAME.UPDATE_LOADING_STATE: return { ...state, ...action.payload }; + case API_ACTION_NAME.SET_ZERO_DOCS_FALLBACK: + return { ...state, zeroDocsFallback: action.payload }; default: return state; } diff --git a/x-pack/plugins/aiops/public/application/utils/url_state.ts b/x-pack/plugins/aiops/public/application/url_state/common.ts similarity index 71% rename from x-pack/plugins/aiops/public/application/utils/url_state.ts rename to x-pack/plugins/aiops/public/application/url_state/common.ts index 22a32a3d610ed..8ca9ec848150c 100644 --- a/x-pack/plugins/aiops/public/application/utils/url_state.ts +++ b/x-pack/plugins/aiops/public/application/url_state/common.ts @@ -38,21 +38,3 @@ export const getDefaultAiOpsListState = ( filters: [], ...overrides, }); - -export interface LogCategorizationPageUrlState { - pageKey: 'logCategorization'; - pageUrlState: LogCategorizationAppState; -} - -export interface LogCategorizationAppState extends AiOpsFullIndexBasedAppState { - field: string | undefined; -} - -export const getDefaultLogCategorizationAppState = ( - overrides?: Partial -): LogCategorizationAppState => { - return { - field: undefined, - ...getDefaultAiOpsListState(overrides), - }; -}; diff --git a/x-pack/plugins/aiops/public/application/url_state/log_pattern_analysis.ts b/x-pack/plugins/aiops/public/application/url_state/log_pattern_analysis.ts new file mode 100644 index 0000000000000..7ea9e1a398e40 --- /dev/null +++ b/x-pack/plugins/aiops/public/application/url_state/log_pattern_analysis.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getDefaultAiOpsListState, type AiOpsFullIndexBasedAppState } from './common'; + +export interface LogCategorizationPageUrlState { + pageKey: 'logCategorization'; + pageUrlState: LogCategorizationAppState; +} + +export interface LogCategorizationAppState extends AiOpsFullIndexBasedAppState { + field: string | undefined; +} + +export const getDefaultLogCategorizationAppState = ( + overrides?: Partial +): LogCategorizationAppState => { + return { + field: undefined, + ...getDefaultAiOpsListState(overrides), + }; +}; diff --git a/x-pack/plugins/aiops/public/application/url_state/log_rate_analysis.ts b/x-pack/plugins/aiops/public/application/url_state/log_rate_analysis.ts new file mode 100644 index 0000000000000..f7691bc2b9d79 --- /dev/null +++ b/x-pack/plugins/aiops/public/application/url_state/log_rate_analysis.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { WindowParameters } from '@kbn/aiops-utils'; + +import { getDefaultAiOpsListState, type AiOpsFullIndexBasedAppState } from './common'; + +export interface LogRateAnalysisPageUrlState { + pageKey: 'logRateAnalysis'; + pageUrlState: LogRateAnalysisAppState; +} +/** + * To avoid long urls, we store the window parameters in the url state not with + * their full parameters names but with abbrevations. `windowParametersToAppState` and + * `appStateToWindowParameters` are used to transform the data structure. + */ +export interface LogRateAnalysisAppState extends AiOpsFullIndexBasedAppState { + /** Window parameters */ + wp?: { + /** Baseline minimum value */ + bMin: number; + /** Baseline maximum value */ + bMax: number; + /** Deviation minimum value */ + dMin: number; + /** Deviation maximum value */ + dMax: number; + }; +} + +/** + * Transforms a full window parameters object to the abbreviated url state version. + */ +export const windowParametersToAppState = (wp?: WindowParameters): LogRateAnalysisAppState['wp'] => + wp && { + bMin: wp.baselineMin, + bMax: wp.baselineMax, + dMin: wp.deviationMin, + dMax: wp.deviationMax, + }; + +/** + * Transforms an abbreviated url state version of window parameters to its full version. + */ +export const appStateToWindowParameters = ( + wp: LogRateAnalysisAppState['wp'] +): WindowParameters | undefined => + wp && { + baselineMin: wp.bMin, + baselineMax: wp.bMax, + deviationMin: wp.dMin, + deviationMax: wp.dMax, + }; + +export const getDefaultLogRateAnalysisAppState = ( + overrides?: Partial +): LogRateAnalysisAppState => { + return { + wp: undefined, + ...getDefaultAiOpsListState(overrides), + }; +}; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx index 0f5303725e514..ce15116d968cb 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/no_change_points_warning.tsx @@ -5,11 +5,13 @@ * 2.0. */ -import React, { type FC } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiEmptyPrompt } from '@elastic/eui'; -export const NoChangePointsWarning: FC = () => { +export const NoChangePointsWarning = (props: { onRenderComplete?: () => void }) => { + props.onRenderComplete?.(); + return ( +export const createCategorizeFieldAction = (coreStart: CoreStart, plugins: AiopsPluginStartDeps) => createAction({ type: ACTION_CATEGORIZE_FIELD, id: ACTION_CATEGORIZE_FIELD, getDisplayName: () => i18n.translate('xpack.aiops.categorizeFieldAction.displayName', { - defaultMessage: 'Categorize field', + defaultMessage: 'Pattern analysis', }), isCompatible: async ({ field }: CategorizeFieldContext) => { return field.esTypes?.includes('text') === true; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 3796af67f8cd2..05cca08d22227 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -30,7 +30,7 @@ import type { } from '../../../../common/api/log_categorization/types'; import { useEuiTheme } from '../../../hooks/use_eui_theme'; -import type { LogCategorizationAppState } from '../../../application/utils/url_state'; +import type { LogCategorizationAppState } from '../../../application/url_state/log_pattern_analysis'; import { MiniHistogram } from '../../mini_histogram'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/index.ts index 550add8802747..ace01d4f03389 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/index.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/index.ts @@ -7,7 +7,7 @@ export type { LogCategorizationAppStateProps } from './log_categorization_app_state'; import { LogCategorizationAppState } from './log_categorization_app_state'; -export { categorizeFieldAction } from './categorize_field_actions'; +export { createCategorizeFieldAction } from './categorize_field_actions'; // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx index 5b37fd019c013..1f3eda09132eb 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -29,7 +29,7 @@ import type { Category, SparkLinesPerCategory } from '../../../common/api/log_ca import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, -} from '../../application/utils/url_state'; +} from '../../application/url_state/log_pattern_analysis'; import { createMergedEsQuery } from '../../application/utils/search_utils'; import { useData } from '../../hooks/use_data'; import { useSearch } from '../../hooks/use_search'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index af3a636be27e8..b07a10a4924f1 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -37,7 +37,7 @@ import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { getDefaultLogCategorizationAppState, type LogCategorizationPageUrlState, -} from '../../application/utils/url_state'; +} from '../../application/url_state/log_pattern_analysis'; import { SearchPanel } from '../search_panel'; import { PageHeader } from '../page_header'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts index 22e8e50ebcf19..36d5eae310a52 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts @@ -15,7 +15,7 @@ import type { Filter } from '@kbn/es-query'; import { getCategoryQuery } from '../../../common/api/log_categorization/get_category_query'; import type { Category } from '../../../common/api/log_categorization/types'; -import type { AiOpsIndexBasedAppState } from '../../application/utils/url_state'; +import type { AiOpsIndexBasedAppState } from '../../application/url_state/common'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; export const QUERY_MODE = { diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx index 0ce422a0fb872..51244a2300634 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx @@ -6,7 +6,7 @@ */ import { isEqual } from 'lodash'; -import React, { useEffect, useMemo, useState, type FC } from 'react'; +import React, { useEffect, useMemo, useRef, useState, type FC } from 'react'; import { EuiEmptyPrompt, EuiHorizontalRule, EuiPanel } from '@elastic/eui'; import type { Moment } from 'moment'; @@ -76,6 +76,8 @@ export interface LogRateAnalysisContentProps { barHighlightColorOverride?: string; /** Optional callback that exposes data of the completed analysis */ onAnalysisCompleted?: (d: LogRateAnalysisResultsData) => void; + /** Optional callback that exposes current window parameters */ + onWindowParametersChange?: (wp?: WindowParameters) => void; /** Identifier to indicate the plugin utilizing the component */ embeddingOrigin: string; } @@ -90,6 +92,7 @@ export const LogRateAnalysisContent: FC = ({ barColorOverride, barHighlightColorOverride, onAnalysisCompleted, + onWindowParametersChange, embeddingOrigin, }) => { const [windowParameters, setWindowParameters] = useState(); @@ -105,6 +108,28 @@ export const LogRateAnalysisContent: FC = ({ setIsBrushCleared(windowParameters === undefined); }, [windowParameters]); + // Window parameters stored in the url state use this components + // `initialAnalysisStart` prop to set the initial params restore from url state. + // To avoid a loop with window parameters being passed around on load, + // the following ref and useEffect are used to check wether it's safe to call + // the `onWindowParametersChange` callback. + const windowParametersTouched = useRef(false); + useEffect(() => { + // Don't continue if window parameters were not touched yet. + // Because they can be reset to `undefined` at a later stage again when a user + // clears the selections, we cannot rely solely on checking if they are + // `undefined`, we need the additional ref to update on the first change. + if (!windowParametersTouched.current && windowParameters === undefined) { + return; + } + + windowParametersTouched.current = true; + + if (onWindowParametersChange) { + onWindowParametersChange(windowParameters); + } + }, [onWindowParametersChange, windowParameters]); + // Checks if `esSearchQuery` is the default empty query passed on from the search bar // and if that's the case fall back to a simpler match all query. const searchQuery = useMemo( diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_page.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_page.tsx index b3c9f256fb2ea..4931503b7366e 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_page.tsx @@ -6,22 +6,26 @@ */ import React, { useCallback, useEffect, useState, FC } from 'react'; +import { isEqual } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexItem, EuiPageBody, EuiPageSection, EuiSpacer } from '@elastic/eui'; import { Filter, FilterStateStore, Query } from '@kbn/es-query'; import { useUrlState, usePageUrlState } from '@kbn/ml-url-state'; - import type { SearchQueryLanguage } from '@kbn/ml-query-utils'; +import type { WindowParameters } from '@kbn/aiops-utils'; + import { useDataSource } from '../../hooks/use_data_source'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { useData } from '../../hooks/use_data'; import { useSearch } from '../../hooks/use_search'; import { - getDefaultAiOpsListState, - type AiOpsPageUrlState, -} from '../../application/utils/url_state'; + getDefaultLogRateAnalysisAppState, + appStateToWindowParameters, + windowParametersToAppState, + type LogRateAnalysisPageUrlState, +} from '../../application/url_state/log_rate_analysis'; import { AIOPS_TELEMETRY_ID } from '../../../common/constants'; import { SearchPanel } from '../search_panel'; @@ -40,9 +44,9 @@ export const LogRateAnalysisPage: FC = ({ stickyHistogram }) => { const { currentSelectedSignificantItem, currentSelectedGroup } = useLogRateAnalysisResultsTableRowContext(); - const [aiopsListState, setAiopsListState] = usePageUrlState( - 'AIOPS_INDEX_VIEWER', - getDefaultAiOpsListState() + const [stateFromUrl, setUrlState] = usePageUrlState( + 'logRateAnalysis', + getDefaultLogRateAnalysisAppState() ); const [globalState, setGlobalState] = useUrlState('_g'); @@ -67,20 +71,20 @@ export const LogRateAnalysisPage: FC = ({ stickyHistogram }) => { setSelectedSavedSearch(null); } - setAiopsListState({ - ...aiopsListState, + setUrlState({ + ...stateFromUrl, searchQuery: searchParams.searchQuery, searchString: searchParams.searchString, searchQueryLanguage: searchParams.queryLanguage, filters: searchParams.filters, }); }, - [selectedSavedSearch, aiopsListState, setAiopsListState] + [selectedSavedSearch, stateFromUrl, setUrlState] ); const { searchQueryLanguage, searchString, searchQuery } = useSearch( { dataView, savedSearch }, - aiopsListState + stateFromUrl ); const { timefilter } = useData( @@ -132,6 +136,14 @@ export const LogRateAnalysisPage: FC = ({ stickyHistogram }) => { }); }, [dataService, searchQueryLanguage, searchString]); + const onWindowParametersHandler = (wp?: WindowParameters) => { + if (!isEqual(wp, stateFromUrl.wp)) { + setUrlState({ + wp: windowParametersToAppState(wp), + }); + } + }; + return ( @@ -148,11 +160,13 @@ export const LogRateAnalysisPage: FC = ({ stickyHistogram }) => { /> diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx index 457419c8b0501..121c19be2ddb1 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx @@ -46,6 +46,7 @@ import { import { useLogRateAnalysisResultsTableRowContext } from '../log_rate_analysis_results_table/log_rate_analysis_results_table_row_provider'; import { FieldFilterPopover } from './field_filter_popover'; +import { LogRateAnalysisTypeCallOut } from './log_rate_analysis_type_callout'; const groupResultsMessage = i18n.translate( 'xpack.aiops.logRateAnalysis.resultsTable.groupedSwitchLabel.groupResults', @@ -209,7 +210,8 @@ export const LogRateAnalysisResults: FC = ({ { [AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN]: embeddingOrigin } ); - const { significantItems } = data; + const { significantItems, zeroDocsFallback } = data; + useEffect( () => setUniqueFieldNames(uniq(significantItems.map((d) => d.fieldName)).sort()), [significantItems] @@ -362,37 +364,13 @@ export const LogRateAnalysisResults: FC = ({ /> - {showLogRateAnalysisResultsTable && ( + {showLogRateAnalysisResultsTable && currentAnalysisType !== undefined && ( <> - - {currentAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE - ? i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutTitle', { - defaultMessage: 'Analysis type: Log rate spike', - }) - : i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutTitle', { - defaultMessage: 'Analysis type: Log rate dip', - })} - - } - color="primary" - iconType="pin" - size="s" - > - - {currentAnalysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE - ? i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutContent', { - defaultMessage: - 'The median log rate in the selected deviation time range is higher than the baseline. Therefore, the analysis results table shows statistically significant items within the deviation time range that are contributors to the spike. The "doc count" column refers to the amount of documents in the deviation time range.', - }) - : i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutContent', { - defaultMessage: - 'The median log rate in the selected deviation time range is lower than the baseline. Therefore, the analysis results table shows statistically significant items within the baseline time range that are less in number or missing within the deviation time range. The "doc count" column refers to the amount of documents in the baseline time range.', - })} - - + )} @@ -490,6 +468,7 @@ export const LogRateAnalysisResults: FC = ({ searchQuery={searchQuery} barColorOverride={barColorOverride} barHighlightColorOverride={barHighlightColorOverride} + zeroDocsFallback={zeroDocsFallback} /> ) : null} {showLogRateAnalysisResultsTable && !groupResults ? ( @@ -501,6 +480,7 @@ export const LogRateAnalysisResults: FC = ({ searchQuery={searchQuery} barColorOverride={barColorOverride} barHighlightColorOverride={barHighlightColorOverride} + zeroDocsFallback={zeroDocsFallback} /> ) : null}
    diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_type_callout.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_type_callout.tsx new file mode 100644 index 0000000000000..2760a27c3d224 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_type_callout.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type FC } from 'react'; + +import { EuiCallOut, EuiText } from '@elastic/eui'; + +import { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from '@kbn/aiops-utils'; +import { i18n } from '@kbn/i18n'; + +interface LogRateAnalysisTypeCallOutProps { + analysisType: LogRateAnalysisType; + zeroDocsFallback: boolean; +} + +export const LogRateAnalysisTypeCallOut: FC = ({ + analysisType, + zeroDocsFallback, +}) => { + let callOutTitle: string; + let callOutText: string; + + if (!zeroDocsFallback && analysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE) { + callOutTitle = i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutTitle', { + defaultMessage: 'Analysis type: Log rate spike', + }); + callOutText = i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutContent', { + defaultMessage: + 'The median log rate in the selected deviation time range is higher than the baseline. Therefore, the analysis results table shows statistically significant items within the deviation time range that are contributors to the spike. The "doc count" column refers to the amount of documents in the deviation time range.', + }); + } else if (!zeroDocsFallback && analysisType === LOG_RATE_ANALYSIS_TYPE.DIP) { + callOutTitle = i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutTitle', { + defaultMessage: 'Analysis type: Log rate dip', + }); + callOutText = i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutContent', { + defaultMessage: + 'The median log rate in the selected deviation time range is lower than the baseline. Therefore, the analysis results table shows statistically significant items within the baseline time range that are less in number or missing within the deviation time range. The "doc count" column refers to the amount of documents in the baseline time range.', + }); + } else if (zeroDocsFallback && analysisType === LOG_RATE_ANALYSIS_TYPE.SPIKE) { + callOutTitle = i18n.translate('xpack.aiops.analysis.analysisTypeSpikeFallbackCallOutTitle', { + defaultMessage: 'Analysis type: Top items for deviation time range', + }); + callOutText = i18n.translate('xpack.aiops.analysis.analysisTypeSpikeCallOutContentFallback', { + defaultMessage: + 'The baseline time range does not contain any documents. Therefore the results show top log message categories and field values for the deviation time range.', + }); + } else if (zeroDocsFallback && analysisType === LOG_RATE_ANALYSIS_TYPE.DIP) { + callOutTitle = i18n.translate('xpack.aiops.analysis.analysisTypeDipFallbackCallOutTitle', { + defaultMessage: 'Analysis type: Top items for baseline time range', + }); + callOutText = i18n.translate('xpack.aiops.analysis.analysisTypeDipCallOutContentFallback', { + defaultMessage: + 'The deviation time range does not contain any documents. Therefore the results show top log message categories and field values for the baseline time range.', + }); + } else { + return null; + } + + return ( + {callOutTitle}} + color="primary" + iconType="pin" + size="s" + > + {callOutText} + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx index d8845145f1ace..b0b4d699919be 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx @@ -51,7 +51,9 @@ const NOT_AVAILABLE = '--'; const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; const DEFAULT_SORT_FIELD = 'pValue'; +const DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK = 'doc_count'; const DEFAULT_SORT_DIRECTION = 'asc'; +const DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK = 'desc'; const TRUNCATE_TEXT_LINES = 3; @@ -66,6 +68,7 @@ interface LogRateAnalysisResultsTableProps { barColorOverride?: string; /** Optional color override for the highlighted bar color for charts */ barHighlightColorOverride?: string; + zeroDocsFallback?: boolean; } export const LogRateAnalysisResultsTable: FC = ({ @@ -77,6 +80,7 @@ export const LogRateAnalysisResultsTable: FC = timeRangeMs, barColorOverride, barHighlightColorOverride, + zeroDocsFallback = false, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); @@ -93,8 +97,12 @@ export const LogRateAnalysisResultsTable: FC = const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); + const [sortField, setSortField] = useState( + zeroDocsFallback ? DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK : DEFAULT_SORT_FIELD + ); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>( + zeroDocsFallback ? DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK : DEFAULT_SORT_DIRECTION + ); const { data, uiSettings, fieldFormats, charts } = useAiopsAppContext(); @@ -236,7 +244,10 @@ export const LogRateAnalysisResultsTable: FC = sortable: true, valign: 'middle', }, - { + ]; + + if (!zeroDocsFallback) { + columns.push({ 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnPValue', width: NARROW_COLUMN_WIDTH, field: 'pValue', @@ -260,8 +271,9 @@ export const LogRateAnalysisResultsTable: FC = render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE, sortable: true, valign: 'middle', - }, - { + }); + + columns.push({ 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact', width: NARROW_COLUMN_WIDTH, field: 'pValue', @@ -291,21 +303,22 @@ export const LogRateAnalysisResultsTable: FC = }, sortable: true, valign: 'middle', - }, - { - 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnAction', - name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnName', { - defaultMessage: 'Actions', - }), - actions: [ - ...(viewInDiscoverAction ? [viewInDiscoverAction] : []), - ...(viewInLogPatternAnalysisAction ? [viewInLogPatternAnalysisAction] : []), - copyToClipBoardAction, - ], - width: ACTIONS_COLUMN_WIDTH, - valign: 'middle', - }, - ]; + }); + } + + columns.push({ + 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnAction', + name: i18n.translate('xpack.aiops.logRateAnalysis.resultsTable.actionsColumnName', { + defaultMessage: 'Actions', + }), + actions: [ + ...(viewInDiscoverAction ? [viewInDiscoverAction] : []), + ...(viewInLogPatternAnalysisAction ? [viewInLogPatternAnalysisAction] : []), + copyToClipBoardAction, + ], + width: ACTIONS_COLUMN_WIDTH, + valign: 'middle', + }); if (isExpandedRow === true) { columns.unshift({ diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx index f6961e49c2c78..957385780ceaa 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx @@ -50,7 +50,9 @@ const MAX_GROUP_BADGES = 5; const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; const DEFAULT_SORT_FIELD = 'pValue'; +const DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK = 'docCount'; const DEFAULT_SORT_DIRECTION = 'asc'; +const DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK = 'desc'; interface LogRateAnalysisResultsTableProps { significantItems: SignificantItem[]; @@ -63,6 +65,7 @@ interface LogRateAnalysisResultsTableProps { barColorOverride?: string; /** Optional color override for the highlighted bar color for charts */ barHighlightColorOverride?: string; + zeroDocsFallback?: boolean; } export const LogRateAnalysisResultsGroupsTable: FC = ({ @@ -74,11 +77,16 @@ export const LogRateAnalysisResultsGroupsTable: FC { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); + const [sortField, setSortField] = useState<'docCount' | 'pValue'>( + zeroDocsFallback ? DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK : DEFAULT_SORT_FIELD + ); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>( + zeroDocsFallback ? DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK : DEFAULT_SORT_DIRECTION + ); const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); @@ -297,7 +305,10 @@ export const LogRateAnalysisResultsGroupsTable: FC pValue?.toPrecision(3) ?? NOT_AVAILABLE, sortable: true, valign: 'top', - }, - { + }); + + columns.push({ 'data-test-subj': 'aiopsLogRateAnalysisResultsTableColumnImpact', width: NARROW_COLUMN_WIDTH, field: 'pValue', @@ -355,21 +367,22 @@ export const LogRateAnalysisResultsGroupsTable: FC { if (tableSettings.page) { diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx index cbfff714d66fb..0392f28184b8b 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx @@ -243,7 +243,7 @@ export const ChartGridEmbeddableWrapper: FC< ) : emptyState ? ( emptyState ) : ( - + )}
    ); diff --git a/x-pack/plugins/aiops/public/hooks/use_search.ts b/x-pack/plugins/aiops/public/hooks/use_search.ts index 609beb6774bc9..8c62db36289fd 100644 --- a/x-pack/plugins/aiops/public/hooks/use_search.ts +++ b/x-pack/plugins/aiops/public/hooks/use_search.ts @@ -11,7 +11,7 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { getEsQueryFromSavedSearch } from '../application/utils/search_utils'; -import type { AiOpsIndexBasedAppState } from '../application/utils/url_state'; +import type { AiOpsIndexBasedAppState } from '../application/url_state/common'; import { useAiopsAppContext } from './use_aiops_app_context'; export const useSearch = ( diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index c896ccc7d5b3e..12a7f659135ae 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -25,49 +25,38 @@ export class AiopsPlugin core: AiopsCoreSetup, { embeddable, cases, licensing, uiActions }: AiopsPluginSetupDeps ) { - firstValueFrom(licensing.license$).then(async (license) => { - if (license.hasAtLeast('platinum')) { - if (embeddable) { - const { registerEmbeddable } = await import('./embeddable/register_embeddable'); - registerEmbeddable(core, embeddable); - } + Promise.all([ + firstValueFrom(licensing.license$), + import('./embeddable/register_embeddable'), + import('./ui_actions'), + import('./cases/register_change_point_charts_attachment'), + core.getStartServices(), + ]).then( + ([ + license, + { registerEmbeddable }, + { registerAiopsUiActions }, + { registerChangePointChartsAttachment }, + [coreStart, pluginStart], + ]) => { + if (license.hasAtLeast('platinum')) { + if (embeddable) { + registerEmbeddable(core, embeddable); + } - if (uiActions) { - const { registerAiopsUiActions } = await import('./ui_actions'); - registerAiopsUiActions(uiActions, core); - } + if (uiActions) { + registerAiopsUiActions(uiActions, coreStart, pluginStart); + } - if (cases) { - const [coreStart, pluginStart] = await core.getStartServices(); - const { registerChangePointChartsAttachment } = await import( - './cases/register_change_point_charts_attachment' - ); - registerChangePointChartsAttachment(cases, coreStart, pluginStart); + if (cases) { + registerChangePointChartsAttachment(cases, coreStart, pluginStart); + } } } - }); + ); } public start(core: CoreStart, plugins: AiopsPluginStartDeps): AiopsPluginStart { - // importing async to keep the aiops plugin size to a minimum - Promise.all([ - import('@kbn/ui-actions-plugin/public'), - import('./components/log_categorization'), - firstValueFrom(plugins.licensing.license$), - ]).then(([uiActionsImports, { categorizeFieldAction }, license]) => { - if (license.hasAtLeast('platinum')) { - const { ACTION_CATEGORIZE_FIELD, CATEGORIZE_FIELD_TRIGGER } = uiActionsImports; - if (plugins.uiActions.hasAction(ACTION_CATEGORIZE_FIELD)) { - plugins.uiActions.unregisterAction(ACTION_CATEGORIZE_FIELD); - } - - plugins.uiActions.addTriggerAction( - CATEGORIZE_FIELD_TRIGGER, - categorizeFieldAction(core, plugins) - ); - } - }); - return { EmbeddableChangePointChart: getEmbeddableChangePointChart(core, plugins), }; diff --git a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx index ae50937921f33..d9401f6064cbf 100644 --- a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx @@ -8,14 +8,16 @@ import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; import { i18n } from '@kbn/i18n'; import { ViewMode } from '@kbn/embeddable-plugin/common'; +import type { CoreStart } from '@kbn/core/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; import type { EditChangePointChartsPanelContext } from '../embeddable/types'; -import type { AiopsCoreSetup } from '../plugin'; +import type { AiopsPluginStartDeps } from '../types'; export const EDIT_CHANGE_POINT_CHARTS_ACTION = 'editChangePointChartsPanelAction'; export function createEditChangePointChartsPanelAction( - getStartServices: AiopsCoreSetup['getStartServices'] + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps ): UiActionsActionDefinition { return { id: 'edit-change-point-charts', @@ -32,8 +34,6 @@ export function createEditChangePointChartsPanelAction( throw new Error('Not possible to execute an action without the embeddable context'); } - const [coreStart, pluginStart] = await getStartServices(); - try { const { resolveEmbeddableChangePointUserInput } = await import( '../embeddable/handle_explicit_input' diff --git a/x-pack/plugins/aiops/public/ui_actions/index.ts b/x-pack/plugins/aiops/public/ui_actions/index.ts index cd00842c662c8..14e1879027fff 100644 --- a/x-pack/plugins/aiops/public/ui_actions/index.ts +++ b/x-pack/plugins/aiops/public/ui_actions/index.ts @@ -7,16 +7,33 @@ import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; +import { + categorizeFieldTrigger, + CATEGORIZE_FIELD_TRIGGER, +} from '@kbn/ml-ui-actions/src/aiops/ui_actions'; + +import type { CoreStart } from '@kbn/core/public'; +import type { AiopsPluginStartDeps } from '../types'; import { createEditChangePointChartsPanelAction } from './edit_change_point_charts_panel'; -import type { AiopsCoreSetup } from '../plugin'; +import { createCategorizeFieldAction } from '../components/log_categorization'; -export function registerAiopsUiActions(uiActions: UiActionsSetup, core: AiopsCoreSetup) { +export function registerAiopsUiActions( + uiActions: UiActionsSetup, + coreStart: CoreStart, + pluginStart: AiopsPluginStartDeps +) { // Initialize actions const editChangePointChartPanelAction = createEditChangePointChartsPanelAction( - core.getStartServices + coreStart, + pluginStart + ); + // // Register actions and triggers + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editChangePointChartPanelAction); + + uiActions.registerTrigger(categorizeFieldTrigger); + + uiActions.addTriggerAction( + CATEGORIZE_FIELD_TRIGGER, + createCategorizeFieldAction(coreStart, pluginStart) ); - // Register actions - uiActions.registerAction(editChangePointChartPanelAction); - // Assign and register triggers - uiActions.attachAction(CONTEXT_MENU_TRIGGER, editChangePointChartPanelAction.id); } diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/index_info_handler.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/index_info_handler.ts index 6ae2a07055aec..3c6ea4d022bb0 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/index_info_handler.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/index_info_handler.ts @@ -7,7 +7,10 @@ import { i18n } from '@kbn/i18n'; -import { updateLoadingStateAction } from '../../../../common/api/log_rate_analysis/actions'; +import { + updateLoadingStateAction, + setZeroDocsFallback, +} from '../../../../common/api/log_rate_analysis/actions'; import type { AiopsLogRateAnalysisApiVersion as ApiVersion } from '../../../../common/api/log_rate_analysis/schema'; import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; @@ -36,6 +39,7 @@ export const indexInfoHandlerFactory = const textFieldCandidates: string[] = []; let totalDocCount = 0; + let zeroDocsFallback = false; if (!requestBody.overrides?.remainingFieldCandidates) { logDebugMessage('Fetch index information.'); @@ -63,7 +67,8 @@ export const indexInfoHandlerFactory = fieldCandidates.push(...indexInfo.fieldCandidates); fieldCandidatesCount = fieldCandidates.length; textFieldCandidates.push(...indexInfo.textFieldCandidates); - totalDocCount = indexInfo.totalDocCount; + totalDocCount = indexInfo.deviationTotalDocCount; + zeroDocsFallback = indexInfo.zeroDocsFallback; } catch (e) { if (!isRequestAbortedError(e)) { logger.error(`Failed to fetch index information, got: \n${e.toString()}`); @@ -96,6 +101,8 @@ export const indexInfoHandlerFactory = }) ); + responseStream.push(setZeroDocsFallback(zeroDocsFallback)); + if (fieldCandidatesCount === 0) { responseStream.endWithUpdatedLoadingState(); } else if (stateHandler.shouldStop()) { @@ -105,5 +112,5 @@ export const indexInfoHandlerFactory = } } - return { fieldCandidates, textFieldCandidates }; + return { fieldCandidates, textFieldCandidates, zeroDocsFallback }; }; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts new file mode 100644 index 0000000000000..10f0b19f07925 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts @@ -0,0 +1,212 @@ +/* + * Copyright 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 { queue } from 'async'; + +import { SIGNIFICANT_ITEM_TYPE, type SignificantItem } from '@kbn/ml-agg-utils'; +import { i18n } from '@kbn/i18n'; + +import { + addSignificantItemsAction, + updateLoadingStateAction, +} from '../../../../common/api/log_rate_analysis/actions'; + +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; + +import { fetchTopCategories } from '../queries/fetch_top_categories'; +import { fetchTopTerms } from '../queries/fetch_top_terms'; + +import type { + AiopsLogRateAnalysisSchema, + AiopsLogRateAnalysisApiVersion as ApiVersion, +} from '../../../../common/api/log_rate_analysis/schema'; + +import { + LOADED_FIELD_CANDIDATES, + MAX_CONCURRENT_QUERIES, + PROGRESS_STEP_P_VALUES, +} from '../response_stream_utils/constants'; +import type { ResponseStreamFetchOptions } from '../response_stream_factory'; + +export const topItemsHandlerFactory = + ({ + abortSignal, + client, + logDebugMessage, + logger, + requestBody, + responseStream, + stateHandler, + version, + }: ResponseStreamFetchOptions) => + async ({ + fieldCandidates, + textFieldCandidates, + }: { + fieldCandidates: string[]; + textFieldCandidates: string[]; + }) => { + let fieldCandidatesCount = fieldCandidates.length; + + // This will store the combined count of detected log patterns and keywords + let fieldValuePairsCount = 0; + + const topCategories: SignificantItem[] = []; + + if (version === '1') { + topCategories.push( + ...((requestBody as AiopsLogRateAnalysisSchema<'1'>).overrides?.significantTerms?.filter( + (d) => d.type === SIGNIFICANT_ITEM_TYPE.LOG_PATTERN + ) ?? []) + ); + } + + if (version === '2') { + topCategories.push( + ...((requestBody as AiopsLogRateAnalysisSchema<'2'>).overrides?.significantItems?.filter( + (d) => d.type === SIGNIFICANT_ITEM_TYPE.LOG_PATTERN + ) ?? []) + ); + } + + // Get categories of text fields + if (textFieldCandidates.length > 0) { + topCategories.push( + ...(await fetchTopCategories( + client, + requestBody, + textFieldCandidates, + logger, + stateHandler.sampleProbability(), + responseStream.pushError, + abortSignal + )) + ); + + if (topCategories.length > 0) { + responseStream.push(addSignificantItemsAction(topCategories, version)); + } + } + + const topTerms: SignificantItem[] = []; + + if (version === '1') { + topTerms.push( + ...((requestBody as AiopsLogRateAnalysisSchema<'1'>).overrides?.significantTerms?.filter( + (d) => d.type === SIGNIFICANT_ITEM_TYPE.KEYWORD + ) ?? []) + ); + } + + if (version === '2') { + topTerms.push( + ...((requestBody as AiopsLogRateAnalysisSchema<'2'>).overrides?.significantItems?.filter( + (d) => d.type === SIGNIFICANT_ITEM_TYPE.KEYWORD + ) ?? []) + ); + } + + const fieldsToSample = new Set(); + + let remainingFieldCandidates: string[]; + let loadingStepSizeTopTerms = PROGRESS_STEP_P_VALUES; + + if (requestBody.overrides?.remainingFieldCandidates) { + fieldCandidates.push(...requestBody.overrides?.remainingFieldCandidates); + remainingFieldCandidates = requestBody.overrides?.remainingFieldCandidates; + fieldCandidatesCount = fieldCandidates.length; + loadingStepSizeTopTerms = + LOADED_FIELD_CANDIDATES + + PROGRESS_STEP_P_VALUES - + (requestBody.overrides?.loaded ?? PROGRESS_STEP_P_VALUES); + } else { + remainingFieldCandidates = fieldCandidates; + } + + logDebugMessage('Fetch p-values.'); + + const topTermsQueue = queue(async function (fieldCandidate: string) { + stateHandler.loaded((1 / fieldCandidatesCount) * loadingStepSizeTopTerms, false); + + let fetchedTopTerms: Awaited>; + + try { + fetchedTopTerms = await fetchTopTerms( + client, + requestBody, + [fieldCandidate], + logger, + stateHandler.sampleProbability(), + responseStream.pushError, + abortSignal + ); + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error(`Failed to fetch p-values for '${fieldCandidate}', got: \n${e.toString()}`); + responseStream.pushError(`Failed to fetch p-values for '${fieldCandidate}'.`); + } + return; + } + + remainingFieldCandidates = remainingFieldCandidates.filter((d) => d !== fieldCandidate); + + if (fetchedTopTerms.length > 0) { + fetchedTopTerms.forEach((d) => { + fieldsToSample.add(d.fieldName); + }); + topTerms.push(...fetchedTopTerms); + + responseStream.push(addSignificantItemsAction(fetchedTopTerms, version)); + } + + responseStream.push( + updateLoadingStateAction({ + ccsWarning: false, + loaded: stateHandler.loaded(), + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.identifiedFieldValuePairs', + { + defaultMessage: + 'Identified {fieldValuePairsCount, plural, one {# significant field/value pair} other {# significant field/value pairs}}.', + values: { + fieldValuePairsCount, + }, + } + ), + remainingFieldCandidates, + }) + ); + }, MAX_CONCURRENT_QUERIES); + + topTermsQueue.push(fieldCandidates, (err) => { + if (err) { + logger.error(`Failed to fetch p-values.', got: \n${err.toString()}`); + responseStream.pushError(`Failed to fetch p-values.`); + topTermsQueue.kill(); + responseStream.end(); + } else if (stateHandler.shouldStop()) { + logDebugMessage('shouldStop fetching p-values.'); + topTermsQueue.kill(); + responseStream.end(); + } + }); + await topTermsQueue.drain(); + + fieldValuePairsCount = topCategories.length + topTerms.length; + + if (fieldValuePairsCount === 0) { + logDebugMessage('Stopping analysis, did not find any categories or terms.'); + responseStream.endWithUpdatedLoadingState(); + return; + } + + return { + fieldValuePairsCount, + significantCategories: topCategories, + significantTerms: topTerms, + }; + }; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts index 0344005c869f8..38377a9e0d32c 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts @@ -11,55 +11,9 @@ import type { ElasticsearchClient } from '@kbn/core/server'; import { paramsSearchQueryMock } from './__mocks__/params_search_query'; -import { fetchIndexInfo, getRandomDocsRequest } from './fetch_index_info'; +import { fetchIndexInfo } from './fetch_index_info'; describe('fetch_index_info', () => { - describe('getRandomDocsRequest', () => { - it('returns the most basic request body for a sample of random documents', () => { - const req = getRandomDocsRequest(paramsSearchQueryMock); - - expect(req).toEqual({ - body: { - _source: false, - fields: ['*'], - query: { - function_score: { - query: { - bool: { - filter: [ - { - bool: { - filter: [], - minimum_should_match: 1, - must_not: [], - should: [{ term: { 'the-term': { value: 'the-value' } } }], - }, - }, - { - range: { - 'the-time-field-name': { - format: 'epoch_millis', - gte: 0, - lte: 50, - }, - }, - }, - ], - }, - }, - random_score: {}, - }, - }, - size: 1000, - track_total_hits: true, - }, - index: paramsSearchQueryMock.index, - ignore_throttled: undefined, - ignore_unavailable: true, - }); - }); - }); - describe('fetchFieldCandidates', () => { it('returns field candidates and total hits', async () => { const esClientFieldCapsMock = jest.fn(() => ({ @@ -99,15 +53,14 @@ describe('fetch_index_info', () => { search: esClientSearchMock, } as unknown as ElasticsearchClient; - const { totalDocCount, fieldCandidates } = await fetchIndexInfo( - esClientMock, - paramsSearchQueryMock - ); + const { baselineTotalDocCount, deviationTotalDocCount, fieldCandidates } = + await fetchIndexInfo(esClientMock, paramsSearchQueryMock); expect(fieldCandidates).toEqual(['myIpFieldName', 'myKeywordFieldName']); - expect(totalDocCount).toEqual(5000000); + expect(baselineTotalDocCount).toEqual(5000000); + expect(deviationTotalDocCount).toEqual(5000000); expect(esClientFieldCapsMock).toHaveBeenCalledTimes(1); - expect(esClientSearchMock).toHaveBeenCalledTimes(1); + expect(esClientSearchMock).toHaveBeenCalledTimes(2); }); }); }); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts index 595e212159437..fd7975aac1b6e 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts @@ -12,14 +12,12 @@ import type { ElasticsearchClient } from '@kbn/core/server'; import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; -import { getQueryWithParams } from './get_query_with_params'; -import { getRequestBase } from './get_request_base'; +import { getRandomDocsRequest } from './get_random_docs_request'; +import { getTotalDocCountRequest } from './get_total_doc_count_request'; // TODO Consolidate with duplicate `fetchPValues` in // `x-pack/plugins/apm/server/routes/correlations/queries/fetch_duration_field_candidates.ts` -const POPULATED_DOC_COUNT_SAMPLE_SIZE = 1000; - const SUPPORTED_ES_FIELD_TYPES = [ ES_FIELD_TYPES.KEYWORD, ES_FIELD_TYPES.IP, @@ -28,30 +26,12 @@ const SUPPORTED_ES_FIELD_TYPES = [ const SUPPORTED_ES_FIELD_TYPES_TEXT = [ES_FIELD_TYPES.TEXT, ES_FIELD_TYPES.MATCH_ONLY_TEXT]; -export const getRandomDocsRequest = ( - params: AiopsLogRateAnalysisSchema -): estypes.SearchRequest => ({ - ...getRequestBase(params), - body: { - fields: ['*'], - _source: false, - query: { - function_score: { - query: getQueryWithParams({ params }), - // @ts-ignore - random_score: {}, - }, - }, - size: POPULATED_DOC_COUNT_SAMPLE_SIZE, - // Used to determine sample probability for follow up queries - track_total_hits: true, - }, -}); - interface IndexInfo { fieldCandidates: string[]; textFieldCandidates: string[]; - totalDocCount: number; + baselineTotalDocCount: number; + deviationTotalDocCount: number; + zeroDocsFallback: boolean; } export const fetchIndexInfo = async ( @@ -95,15 +75,24 @@ export const fetchIndexInfo = async ( allFieldNames.push(key); }); + // Get the total doc count for the baseline time range + const respBaselineTotalDocCount = await esClient.search( + getTotalDocCountRequest({ ...params, start: params.baselineMin, end: params.baselineMax }), + { + signal: abortSignal, + maxRetries: 0, + } + ); + // Only the deviation window will be used to identify field candidates and sample probability based on total doc count. - const resp = await esClient.search( + const respDeviationRandomDocs = await esClient.search( getRandomDocsRequest({ ...params, start: params.deviationMin, end: params.deviationMax }), { signal: abortSignal, maxRetries: 0, } ); - const sampledDocs = resp.hits.hits.map((d) => d.fields ?? {}); + const sampledDocs = respDeviationRandomDocs.hits.hits.map((d) => d.fields ?? {}); const textFieldCandidatesOverridesWithKeywordPostfix = textFieldCandidatesOverrides.map( (d) => `${d}.keyword` @@ -127,11 +116,16 @@ export const fetchIndexInfo = async ( } }); - const totalDocCount = (resp.hits.total as estypes.SearchTotalHits).value; + const baselineTotalDocCount = (respBaselineTotalDocCount.hits.total as estypes.SearchTotalHits) + .value; + const deviationTotalDocCount = (respDeviationRandomDocs.hits.total as estypes.SearchTotalHits) + .value; return { fieldCandidates: [...finalFieldCandidates], textFieldCandidates: [...finalTextFieldCandidates], - totalDocCount, + baselineTotalDocCount, + deviationTotalDocCount, + zeroDocsFallback: baselineTotalDocCount === 0 || deviationTotalDocCount === 0, }; }; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_categories.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_categories.ts new file mode 100644 index 0000000000000..2933e5d3e414a --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_categories.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { uniq } from 'lodash'; + +import { ElasticsearchClient } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; +import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils'; + +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; + +import { fetchCategories } from './fetch_categories'; + +export const fetchTopCategories = async ( + esClient: ElasticsearchClient, + params: AiopsLogRateAnalysisSchema, + fieldNames: string[], + logger: Logger, + // The default value of 1 means no sampling will be used + sampleProbability: number = 1, + emitError: (m: string) => void, + abortSignal?: AbortSignal +) => { + const categoriesOverall = await fetchCategories( + esClient, + params, + fieldNames, + logger, + sampleProbability, + emitError, + abortSignal + ); + + if (categoriesOverall.length !== fieldNames.length) return []; + + const topCategories: SignificantItem[] = []; + + // Using for...of to allow `await` within the loop. + for (const [i, fieldName] of fieldNames.entries()) { + if (categoriesOverall[i].categories.length === 0) { + continue; + } + + // Get all unique keys + const allKeys: string[] = uniq(categoriesOverall[i].categories.map((cd) => cd.key)); + + allKeys.forEach((key) => { + const categoryData = categoriesOverall[i].categories.find((c) => c.key === key); + + topCategories.push({ + key, + fieldName, + fieldValue: categoryData?.examples[0] ?? '', + doc_count: categoryData?.count ?? 0, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + type: SIGNIFICANT_ITEM_TYPE.LOG_PATTERN, + }); + }); + } + + return topCategories; +}; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts new file mode 100644 index 0000000000000..d1b8007249136 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts @@ -0,0 +1,162 @@ +/* + * Copyright 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 { uniqBy } from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ElasticsearchClient } from '@kbn/core/server'; + +import type { Logger } from '@kbn/logging'; +import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils'; +import { + createRandomSamplerWrapper, + type RandomSamplerWrapper, +} from '@kbn/ml-random-sampler-utils'; + +import { RANDOM_SAMPLER_SEED } from '../../../../common/constants'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; + +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; + +import { getQueryWithParams } from './get_query_with_params'; +import { getRequestBase } from './get_request_base'; + +// TODO Consolidate with duplicate `fetchDurationFieldCandidates` in +// `x-pack/plugins/apm/server/routes/correlations/queries/fetch_failed_events_correlation_p_values.ts` + +export const getTopTermRequest = ( + params: AiopsLogRateAnalysisSchema, + fieldName: string, + { wrap }: RandomSamplerWrapper +): estypes.SearchRequest => { + const query = getQueryWithParams({ + params, + }); + + const timeFieldName = params.timeFieldName ?? '@timestamp'; + + let filter: estypes.QueryDslQueryContainer[] = []; + + if (query.bool && Array.isArray(query.bool.filter)) { + filter = query.bool.filter.filter((d) => Object.keys(d)[0] !== 'range'); + + query.bool.filter = [ + ...filter, + { + range: { + [timeFieldName]: { + gte: params.deviationMin, + lt: params.deviationMax, + format: 'epoch_millis', + }, + }, + }, + ]; + } + + const termAgg: Record<'log_rate_top_terms', estypes.AggregationsAggregationContainer> = { + log_rate_top_terms: { + terms: { + field: fieldName, + size: 10, + }, + }, + }; + + const body = { + query, + size: 0, + aggs: wrap(termAgg), + }; + + return { + ...getRequestBase(params), + body, + }; +}; + +interface Aggs extends estypes.AggregationsLongTermsAggregate { + buckets: estypes.AggregationsLongTermsBucket[]; +} + +export const fetchTopTerms = async ( + esClient: ElasticsearchClient, + params: AiopsLogRateAnalysisSchema, + fieldNames: string[], + logger: Logger, + // The default value of 1 means no sampling will be used + sampleProbability: number = 1, + emitError: (m: string) => void, + abortSignal?: AbortSignal +): Promise => { + const randomSamplerWrapper = createRandomSamplerWrapper({ + probability: sampleProbability, + seed: RANDOM_SAMPLER_SEED, + }); + + const result: SignificantItem[] = []; + + const settledPromises = await Promise.allSettled( + fieldNames.map((fieldName) => + esClient.search(getTopTermRequest(params, fieldName, randomSamplerWrapper), { + signal: abortSignal, + maxRetries: 0, + }) + ) + ); + + function reportError(fieldName: string, error: unknown) { + if (!isRequestAbortedError(error)) { + logger.error( + `Failed to fetch term aggregation for fieldName "${fieldName}", got: \n${JSON.stringify( + error, + null, + 2 + )}` + ); + emitError(`Failed to fetch term aggregation for fieldName "${fieldName}".`); + } + } + + for (const [index, settledPromise] of settledPromises.entries()) { + const fieldName = fieldNames[index]; + + if (settledPromise.status === 'rejected') { + reportError(fieldName, settledPromise.reason); + // Still continue the analysis even if individual p-value queries fail. + continue; + } + + const resp = settledPromise.value; + + if (resp.aggregations === undefined) { + reportError(fieldName, resp); + // Still continue the analysis even if individual p-value queries fail. + continue; + } + + const overallResult = ( + randomSamplerWrapper.unwrap(resp.aggregations) as Record<'log_rate_top_terms', Aggs> + ).log_rate_top_terms; + + for (const bucket of overallResult.buckets) { + result.push({ + key: `${fieldName}:${String(bucket.key)}`, + type: SIGNIFICANT_ITEM_TYPE.KEYWORD, + fieldName, + fieldValue: String(bucket.key), + doc_count: bucket.doc_count, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }); + } + } + + return uniqBy(result, (d) => `${d.fieldName},${d.fieldValue}`); +}; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.test.ts new file mode 100644 index 0000000000000..6e68c789142c5 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { paramsSearchQueryMock } from './__mocks__/params_search_query'; + +import { getRandomDocsRequest } from './get_random_docs_request'; + +describe('getRandomDocsRequest', () => { + it('returns the most basic request body for a sample of random documents', () => { + const req = getRandomDocsRequest(paramsSearchQueryMock); + + expect(req).toEqual({ + body: { + _source: false, + fields: ['*'], + query: { + function_score: { + query: { + bool: { + filter: [ + { + bool: { + filter: [], + minimum_should_match: 1, + must_not: [], + should: [{ term: { 'the-term': { value: 'the-value' } } }], + }, + }, + { + range: { + 'the-time-field-name': { + format: 'epoch_millis', + gte: 0, + lte: 50, + }, + }, + }, + ], + }, + }, + random_score: {}, + }, + }, + size: 1000, + track_total_hits: true, + }, + index: paramsSearchQueryMock.index, + ignore_throttled: undefined, + ignore_unavailable: true, + }); + }); +}); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.ts new file mode 100644 index 0000000000000..7c1abdfd52667 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_random_docs_request.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; + +import { getQueryWithParams } from './get_query_with_params'; +import { getRequestBase } from './get_request_base'; + +const POPULATED_DOC_COUNT_SAMPLE_SIZE = 1000; + +export const getRandomDocsRequest = ( + params: AiopsLogRateAnalysisSchema +): estypes.SearchRequest => ({ + ...getRequestBase(params), + body: { + fields: ['*'], + _source: false, + query: { + function_score: { + query: getQueryWithParams({ params }), + // @ts-ignore + random_score: {}, + }, + }, + size: POPULATED_DOC_COUNT_SAMPLE_SIZE, + // Used to determine sample probability for follow up queries + track_total_hits: true, + }, +}); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_total_doc_count_request.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_total_doc_count_request.ts new file mode 100644 index 0000000000000..e9b0fc853535f --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_total_doc_count_request.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; + +import { getQueryWithParams } from './get_query_with_params'; +import { getRequestBase } from './get_request_base'; + +export const getTotalDocCountRequest = ( + params: AiopsLogRateAnalysisSchema +): estypes.SearchRequest => ({ + ...getRequestBase(params), + body: { + fields: ['*'], + _source: false, + query: getQueryWithParams({ params }), + size: 0, + track_total_hits: true, + }, +}); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/response_stream_factory.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/response_stream_factory.ts index f54c2e93dd29e..0dce9c7cf34d1 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/response_stream_factory.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/response_stream_factory.ts @@ -22,6 +22,7 @@ import { groupingHandlerFactory } from './analysis_handlers/grouping_handler'; import { histogramHandlerFactory } from './analysis_handlers/histogram_handler'; import { overridesHandlerFactory } from './analysis_handlers/overrides_handler'; import { significantItemsHandlerFactory } from './analysis_handlers/significant_items_handler'; +import { topItemsHandlerFactory } from './analysis_handlers/top_items_handler'; import { overallHistogramHandlerFactory } from './analysis_handlers/overall_histogram_handler'; import { logDebugMessageFactory, @@ -131,6 +132,7 @@ export const responseStreamFactory = (options: ResponseStr overallHistogramHandler: overallHistogramHandlerFactory(streamFetchOptions), overridesHandler: overridesHandlerFactory(streamFetchOptions), significantItemsHandler: significantItemsHandlerFactory(streamFetchOptions), + topItemsHandler: topItemsHandlerFactory(streamFetchOptions), }, responseWithHeaders, }; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts index 3c1bcde38ebd6..e0a9b9c3e9803 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts @@ -92,7 +92,9 @@ export function routeHandlerFactory( } // Step 2: Significant categories and terms - const significantItemsObj = await analysis.significantItemsHandler(indexInfo); + const significantItemsObj = indexInfo.zeroDocsFallback + ? await analysis.topItemsHandler(indexInfo) + : await analysis.significantItemsHandler(indexInfo); if (!significantItemsObj) { return; 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 e58b795863e48..eb3132eba5413 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 @@ -242,6 +242,11 @@ describe('mappingFromFieldMap', () => { }, reason: { type: 'keyword', + fields: { + text: { + type: 'match_only_text', + }, + }, }, rule: { properties: { diff --git a/x-pack/plugins/alerting/server/constants/plugin.ts b/x-pack/plugins/alerting/common/constants/plugin.ts similarity index 90% rename from x-pack/plugins/alerting/server/constants/plugin.ts rename to x-pack/plugins/alerting/common/constants/plugin.ts index 05fd33ebf8dd3..e8136f257d465 100644 --- a/x-pack/plugins/alerting/server/constants/plugin.ts +++ b/x-pack/plugins/alerting/common/constants/plugin.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LicenseType } from '@kbn/licensing-plugin/server'; +import type { LicenseType } from '@kbn/licensing-plugin/server'; export const PLUGIN = { ID: 'alerting', diff --git a/x-pack/plugins/alerting/common/maintenance_window.ts b/x-pack/plugins/alerting/common/maintenance_window.ts index 1f5bffea77081..8b7646670cc76 100644 --- a/x-pack/plugins/alerting/common/maintenance_window.ts +++ b/x-pack/plugins/alerting/common/maintenance_window.ts @@ -14,6 +14,13 @@ export enum MaintenanceWindowStatus { Archived = 'archived', } +export const filterStateStore = { + APP_STATE: 'appState', + GLOBAL_STATE: 'globalState', +} as const; + +export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; + export interface MaintenanceWindowModificationMetadata { createdBy: string | null; updatedBy: string | null; @@ -26,6 +33,23 @@ export interface DateRange { lte: string; } +export interface ScopeQueryFilter { + query?: Record; + meta: Record; + $state?: { + store: FilterStateStore; + }; +} + +export interface ScopedQueryAttributes { + kql: string; + filters: ScopeQueryFilter[]; + dsl?: string; +} + +/** + * @deprecated Use the data/maintenance_window types instead + */ export interface MaintenanceWindowSOProperties { title: string; enabled: boolean; @@ -34,11 +58,18 @@ export interface MaintenanceWindowSOProperties { events: DateRange[]; rRule: RRuleParams; categoryIds?: string[] | null; + scopedQuery?: ScopedQueryAttributes | null; } +/** + * @deprecated Use the data/maintenance_window types instead + */ export type MaintenanceWindowSOAttributes = MaintenanceWindowSOProperties & MaintenanceWindowModificationMetadata; +/** + * @deprecated Use the application/maintenance_window types instead + */ export type MaintenanceWindow = MaintenanceWindowSOAttributes & { status: MaintenanceWindowStatus; eventStartTime: string | null; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.ts new file mode 100644 index 0000000000000..6c32b4867cc0d --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/latest.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { filterStateStore } from './v1'; +export type { FilterStateStore } from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts new file mode 100644 index 0000000000000..bce6890c22f2c --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/constants/v1.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const filterStateStore = { + APP_STATE: 'appState', + GLOBAL_STATE: 'globalState', +} as const; + +export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts new file mode 100644 index 0000000000000..093299dbe66f2 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { filterStateStore } from './constants/latest'; +export type { FilterStateStore } from './constants/latest'; +export { alertsFilterQuerySchema } from './schemas/latest'; + +export { filterStateStore as filterStateStoreV1 } from './constants/v1'; +export type { FilterStateStore as FilterStateStoreV1 } from './constants/v1'; +export { alertsFilterQuerySchema as alertsFilterQuerySchemaV1 } from './schemas/v1'; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/latest.ts new file mode 100644 index 0000000000000..b9ae85bd590cf --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/latest.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { alertsFilterQuerySchema } from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts new file mode 100644 index 0000000000000..08614efb96b70 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/alerts_filter_query/schemas/v1.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { filterStateStore } from '..'; + +export const alertsFilterQuerySchema = schema.object({ + kql: schema.string(), + filters: schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + $state: schema.maybe( + schema.object({ + store: schema.oneOf([ + schema.literal(filterStateStore.APP_STATE), + schema.literal(filterStateStore.GLOBAL_STATE), + ]), + }) + ), + }) + ), + dsl: schema.maybe(schema.string()), +}); diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/create/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/create/schemas/v1.ts index f10eb420963ed..a715bd13f2f75 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/create/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/create/schemas/v1.ts @@ -8,10 +8,12 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowCategoryIdsSchemaV1 } from '../../../shared'; import { rRuleRequestSchemaV1 } from '../../../../r_rule'; +import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query'; export const createBodySchema = schema.object({ title: schema.string(), duration: schema.number(), r_rule: rRuleRequestSchemaV1, category_ids: maintenanceWindowCategoryIdsSchemaV1, + scoped_query: schema.maybe(schema.nullable(alertsFilterQuerySchemaV1)), }); diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/update/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/update/schemas/v1.ts index 970cd34424576..ac61048d760a1 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/apis/update/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/apis/update/schemas/v1.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowCategoryIdsSchemaV1 } from '../../../shared'; import { rRuleRequestSchemaV1 } from '../../../../r_rule'; +import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query'; export const updateParamsSchema = schema.object({ id: schema.string(), @@ -19,4 +20,5 @@ export const updateBodySchema = schema.object({ duration: schema.maybe(schema.number()), r_rule: schema.maybe(rRuleRequestSchemaV1), category_ids: maintenanceWindowCategoryIdsSchemaV1, + scoped_query: schema.maybe(schema.nullable(alertsFilterQuerySchemaV1)), }); diff --git a/x-pack/plugins/alerting/common/routes/maintenance_window/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/maintenance_window/response/schemas/v1.ts index 410a1dc1e439d..648b2b806978f 100644 --- a/x-pack/plugins/alerting/common/routes/maintenance_window/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/maintenance_window/response/schemas/v1.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowStatusV1 } from '..'; import { maintenanceWindowCategoryIdsSchemaV1 } from '../../shared'; import { rRuleResponseSchemaV1 } from '../../../r_rule'; +import { alertsFilterQuerySchemaV1 } from '../../../alerts_filter_query'; export const maintenanceWindowEventSchema = schema.object({ gte: schema.string(), @@ -36,4 +37,5 @@ export const maintenanceWindowResponseSchema = schema.object({ schema.literal(maintenanceWindowStatusV1.ARCHIVED), ]), category_ids: maintenanceWindowCategoryIdsSchemaV1, + scoped_query: schema.maybe(schema.nullable(alertsFilterQuerySchemaV1)), }); diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts index 751f2e8c38780..30a294d5c0527 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { validateDurationV1, validateHoursV1, validateTimezoneV1 } from '../../../validation'; import { notifyWhenSchemaV1 } from '../../../response'; -import { filterStateStore } from '../../../common/constants/v1'; +import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query'; export const actionFrequencySchema = schema.object({ summary: schema.boolean(), @@ -17,26 +17,7 @@ export const actionFrequencySchema = schema.object({ }); export const actionAlertsFilterSchema = schema.object({ - query: schema.maybe( - schema.object({ - kql: schema.string(), - filters: schema.arrayOf( - schema.object({ - query: schema.maybe(schema.recordOf(schema.string(), schema.any())), - meta: schema.recordOf(schema.string(), schema.any()), - $state: schema.maybe( - schema.object({ - store: schema.oneOf([ - schema.literal(filterStateStore.APP_STATE), - schema.literal(filterStateStore.GLOBAL_STATE), - ]), - }) - ), - }) - ), - dsl: schema.maybe(schema.string()), - }) - ), + query: schema.maybe(alertsFilterQuerySchemaV1), timeframe: schema.maybe( schema.object({ days: schema.arrayOf( diff --git a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts index 0e043aa217667..1c7b202f59060 100644 --- a/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/response/schemas/v1.ts @@ -7,13 +7,13 @@ import { schema } from '@kbn/config-schema'; import { rRuleResponseSchemaV1 } from '../../../r_rule'; +import { alertsFilterQuerySchemaV1 } from '../../../alerts_filter_query'; import { ruleNotifyWhen as ruleNotifyWhenV1, ruleExecutionStatusValues as ruleExecutionStatusValuesV1, ruleExecutionStatusErrorReason as ruleExecutionStatusErrorReasonV1, ruleExecutionStatusWarningReason as ruleExecutionStatusWarningReasonV1, ruleLastRunOutcomeValues as ruleLastRunOutcomeValuesV1, - filterStateStore as filterStateStoreV1, } from '../../common/constants/v1'; import { validateNotifyWhenV1 } from '../../validation'; @@ -41,25 +41,7 @@ const actionFrequencySchema = schema.object({ }); const actionAlertsFilterSchema = schema.object({ - query: schema.maybe( - schema.object({ - kql: schema.string(), - filters: schema.arrayOf( - schema.object({ - query: schema.maybe(schema.recordOf(schema.string(), schema.any())), - meta: schema.recordOf(schema.string(), schema.any()), - $state: schema.maybe( - schema.object({ - store: schema.oneOf([ - schema.literal(filterStateStoreV1.APP_STATE), - schema.literal(filterStateStoreV1.GLOBAL_STATE), - ]), - }) - ), - }) - ), - }) - ), + query: schema.maybe(alertsFilterQuerySchemaV1), timeframe: schema.maybe( schema.object({ days: schema.arrayOf( diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.json b/x-pack/plugins/alerting/docs/openapi/bundled.json index 6e09c658ae407..8688c79a8122b 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.json +++ b/x-pack/plugins/alerting/docs/openapi/bundled.json @@ -1,9 +1,9 @@ { - "openapi": "3.0.1", + "openapi": "3.1.0", "info": { "title": "Alerting", "description": "OpenAPI schema for alerting endpoints", - "version": "0.1", + "version": "0.2", "contact": { "name": "Alerting Team" }, @@ -239,7 +239,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } } ], @@ -496,9 +498,11 @@ "description": "The default operator to use for the simple_query_string.", "schema": { "type": "string", - "default": "OR" - }, - "example": "OR" + "default": "OR", + "examples": [ + "AND" + ] + } }, { "name": "fields", @@ -541,9 +545,11 @@ "description": "The page number to return.", "schema": { "type": "integer", - "default": 1 - }, - "example": 1 + "default": 1, + "examples": [ + 1 + ] + } }, { "name": "per_page", @@ -551,16 +557,21 @@ "description": "The number of rules to return per page.", "schema": { "type": "integer", - "default": 20 - }, - "example": 20 + "default": 20, + "examples": [ + 20 + ] + } }, { "name": "search", "in": "query", "description": "An Elasticsearch simple_query_string query that filters the objects in the response.", "schema": { - "type": "string" + "type": "string", + "examples": [ + "threshold +-test*" + ] } }, { @@ -570,7 +581,10 @@ "schema": { "oneOf": [ { - "type": "string" + "type": "string", + "examples": [ + "name" + ] }, { "type": "array", @@ -599,9 +613,11 @@ "asc", "desc" ], - "default": "desc" - }, - "example": "asc" + "default": "desc", + "examples": [ + "asc" + ] + } } ], "responses": { @@ -684,7 +700,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -694,7 +712,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } }, @@ -704,7 +724,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -714,7 +736,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } }, @@ -724,7 +748,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -734,7 +760,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } } @@ -743,12 +771,16 @@ "has_permanent_encryption_key": { "type": "boolean", "description": "If `false`, the encrypted saved object plugin does not have a permanent encryption key.", - "example": true + "examples": [ + true + ] }, "is_sufficiently_secure": { "type": "boolean", "description": "If `false`, security is enabled but TLS is not.", - "example": true + "examples": [ + true + ] } } }, @@ -1030,7 +1062,9 @@ "minimum_license_required": { "description": "The subscriptions required to use the rule type.", "type": "string", - "example": "basic" + "examples": [ + "basic" + ] }, "name": { "description": "The descriptive name of the rule type.", @@ -1039,7 +1073,9 @@ "producer": { "description": "An identifier for the application that produces this rule type.", "type": "string", - "example": "stackAlerts" + "examples": [ + "stackAlerts" + ] }, "recovery_action_group": { "description": "An action group to use when an alert goes from an active state to an inactive one.", @@ -1055,7 +1091,9 @@ }, "rule_task_timeout": { "type": "string", - "example": "5m" + "examples": [ + "5m" + ] } } } @@ -1290,7 +1328,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1329,7 +1369,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1375,7 +1417,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } }, { @@ -1464,7 +1508,9 @@ "interval": { "type": "string", "description": "The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.", - "example": "10s" + "examples": [ + "10s" + ] } } }, @@ -1529,7 +1575,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1601,7 +1649,9 @@ "interval": { "type": "string", "description": "The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.", - "example": "1d" + "examples": [ + "1d" + ] } } }, @@ -1668,7 +1718,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1712,7 +1764,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1756,7 +1810,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1800,7 +1856,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } } ], @@ -1840,9 +1898,11 @@ "description": "The default operator to use for the `simple_query_string`.", "schema": { "type": "string", - "default": "OR" - }, - "example": "OR" + "default": "OR", + "examples": [ + "OR" + ] + } }, { "name": "fields", @@ -1885,9 +1945,11 @@ "description": "The page number to return.", "schema": { "type": "integer", - "default": 1 - }, - "example": 1 + "default": 1, + "examples": [ + 1 + ] + } }, { "name": "per_page", @@ -1895,9 +1957,11 @@ "description": "The number of alerts to return per page.", "schema": { "type": "integer", - "default": 20 - }, - "example": 20 + "default": 20, + "examples": [ + 20 + ] + } }, { "name": "search", @@ -1943,9 +2007,11 @@ "asc", "desc" ], - "default": "desc" - }, - "example": "asc" + "default": "desc", + "examples": [ + "asc" + ] + } } ], "responses": { @@ -2021,7 +2087,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -2031,7 +2099,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } }, @@ -2041,7 +2111,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -2051,7 +2123,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } }, @@ -2061,7 +2135,9 @@ "properties": { "status": { "type": "string", - "example": "ok", + "examples": [ + "ok" + ], "enum": [ "error", "ok", @@ -2071,7 +2147,9 @@ "timestamp": { "type": "string", "format": "date-time", - "example": "2023-01-13T01:28:00.280Z" + "examples": [ + "2023-01-13T01:28:00.280Z" + ] } } } @@ -2080,12 +2158,16 @@ "hasPermanentEncryptionKey": { "type": "boolean", "description": "If `false`, the encrypted saved object plugin does not have a permanent encryption key.", - "example": true + "examples": [ + true + ] }, "isSufficientlySecure": { "type": "boolean", "description": "If `false`, security is enabled but TLS is not.", - "example": true + "examples": [ + true + ] } } } @@ -2278,7 +2360,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } }, { @@ -2288,7 +2372,9 @@ "required": true, "schema": { "type": "string", - "example": "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2" + "examples": [ + "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2" + ] } } ], @@ -2332,7 +2418,9 @@ "required": true, "schema": { "type": "string", - "example": "41893910-6bca-11eb-9e0d-85d233e3ee35" + "examples": [ + "41893910-6bca-11eb-9e0d-85d233e3ee35" + ] } }, { @@ -2342,7 +2430,9 @@ "required": true, "schema": { "type": "string", - "example": "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2" + "examples": [ + "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2" + ] } } ], @@ -2373,7 +2463,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "parameters": { @@ -2393,7 +2484,9 @@ "required": true, "schema": { "type": "string", - "example": "default" + "examples": [ + "default" + ] } }, "rule_id": { @@ -2403,7 +2496,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } }, "alert_id": { @@ -2413,7 +2508,9 @@ "required": true, "schema": { "type": "string", - "example": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + "examples": [ + "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74" + ] } } }, @@ -2426,8 +2523,10 @@ "type": "object", "properties": { "alias": { - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "controlledBy": { "type": "string" @@ -2480,19 +2579,27 @@ "onActiveAlert", "onThrottleInterval" ], - "example": "onActiveAlert" + "examples": [ + "onActiveAlert" + ] }, "throttle": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "The throttle interval, which defines how often an alert generates repeated actions. It is specified in seconds, minutes, hours, or days and is applicable only if `notify_when` is set to `onThrottleInterval`. NOTE: You cannot specify the throttle interval at both the rule and action level. The recommended method is to set it for each action. If you set it at the rule level then update the rule in Kibana, it is automatically changed to use action-specific values.\n", - "nullable": true, "default": null, - "example": "10m" + "examples": [ + "10m" + ] }, "actions": { - "type": "array", + "type": [ + "array", + "null" + ], "default": [], - "nullable": true, "items": { "type": "object", "required": [ @@ -2532,12 +2639,14 @@ "items": { "type": "integer" }, - "example": [ - 1, - 2, - 3, - 4, - 5 + "examples": [ + [ + 1, + 2, + 3, + 4, + 5 + ] ] }, "hours": { @@ -2547,19 +2656,25 @@ "end": { "type": "string", "description": "The end of the time frame in 24-hour notation (`hh:mm`).", - "example": "17:00" + "examples": [ + "17:00" + ] }, "start": { "type": "string", "description": "The start of the time frame in 24-hour notation (`hh:mm`).", - "example": "08:00" + "examples": [ + "08:00" + ] } } }, "timezone": { "type": "string", "description": "The ISO time zone for the `hours` values. Values such as `UTC` and `UTC+1` also work but lack built-in daylight savings time support and are not recommended.\n", - "example": "Europe/Madrid" + "examples": [ + "Europe/Madrid" + ] } } } @@ -2568,7 +2683,9 @@ "connector_type_id": { "type": "string", "description": "The type of connector. This property appears in responses but cannot be set in requests.", - "example": ".server-log", + "examples": [ + ".server-log" + ], "readOnly": true }, "frequency": { @@ -2594,12 +2711,16 @@ "group": { "type": "string", "description": "The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to `default`.\n", - "example": "default" + "examples": [ + "default" + ] }, "id": { "type": "string", "description": "The identifier for the connector saved object.", - "example": "9dca3e00-74f5-11ed-9801-35303b735aef" + "examples": [ + "9dca3e00-74f5-11ed-9801-35303b735aef" + ] }, "params": { "type": "object", @@ -2609,7 +2730,9 @@ "uuid": { "type": "string", "description": "A universally unique identifier (UUID) for the action.", - "example": "1c7a1280-f28c-4e06-96b2-e4e5f05d1d61" + "examples": [ + "1c7a1280-f28c-4e06-96b2-e4e5f05d1d61" + ] } } } @@ -2632,7 +2755,9 @@ "properties": { "interval": { "type": "string", - "example": "1m" + "examples": [ + "1m" + ] } } }, @@ -3210,7 +3335,9 @@ "timewindowsize": { "description": "The size of the time window (in `timeWindowUnit` units), which determines how far back to search for documents. Generally it should be a value higher than the rule check interval to avoid gaps in detection.\n", "type": "integer", - "example": 5 + "examples": [ + 5 + ] }, "timewindowunit": { "description": "The type of units for the time window: seconds, minutes, hours, or days.\n", @@ -3221,7 +3348,9 @@ "h", "d" ], - "example": "m" + "examples": [ + "m" + ] }, "size": { "description": "The number of documents to pass to the configured actions when the threshold condition is met.\n", @@ -3247,7 +3376,9 @@ "type": "array", "items": { "type": "integer", - "example": 4000 + "examples": [ + 4000 + ] } }, "thresholdcomparator": { @@ -3261,7 +3392,9 @@ "between", "notBetween" ], - "example": ">" + "examples": [ + ">" + ] }, "params_es_query_rule": { "oneOf": [ @@ -3309,12 +3442,16 @@ "enum": [ "esqlQuery" ], - "example": "esqlQuery" + "examples": [ + "esqlQuery" + ] }, "size": { "type": "integer", "description": "When `searchType` is `esqlQuery`, this property is required but it does not affect the rule behavior.\n", - "example": 0 + "examples": [ + 0 + ] }, "termSize": { "$ref": "#/components/schemas/termsize" @@ -3334,7 +3471,9 @@ "enum": [ ">" ], - "example": ">" + "examples": [ + ">" + ] }, "timeField": { "$ref": "#/components/schemas/timefield" @@ -3400,7 +3539,9 @@ "properties": { "language": { "type": "string", - "example": "kuery" + "examples": [ + "kuery" + ] }, "query": { "type": "string" @@ -3415,7 +3556,9 @@ "enum": [ "searchSource" ], - "example": "searchSource" + "examples": [ + "searchSource" + ] }, "size": { "$ref": "#/components/schemas/size" @@ -3493,7 +3636,9 @@ "esQuery" ], "default": "esQuery", - "example": "esQuery" + "examples": [ + "esQuery" + ] }, "size": { "$ref": "#/components/schemas/size" @@ -6318,58 +6463,82 @@ "api_key_created_by_user": { "type": "boolean", "description": "Indicates whether the API key that is associated with the rule was created by the user.", - "example": false + "examples": [ + false + ] }, "api_key_owner": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "The owner of the API key that is associated with the rule and used to run background tasks.\n", - "nullable": true, - "example": "elastic" + "examples": [ + "elastic" + ] }, "consumer": { "type": "string", "description": "The application or feature that owns the rule. For example, `alerts`, `apm`, `discover`, `infrastructure`, `logs`, `metrics`, `ml`, `monitoring`, `securitySolution`, `siem`, `stackAlerts`, or `uptime`.", - "example": "alerts" + "examples": [ + "alerts" + ] }, "created_at": { "type": "string", "description": "The date and time that the rule was created.", "format": "date-time", - "example": "2022-12-05T23:36:58.284Z" + "examples": [ + "2022-12-05T23:36:58.284Z" + ] }, "created_by": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "The identifier for the user that created the rule.", - "nullable": true, - "example": "elastic" + "examples": [ + "elastic" + ] }, "enabled": { "type": "boolean", "description": "Indicates whether the rule is currently enabled.", - "example": true + "examples": [ + true + ] }, "execution_status": { "type": "object", "properties": { "last_duration": { "type": "integer", - "example": 55 + "examples": [ + 55 + ] }, "last_execution_date": { "type": "string", "format": "date-time", - "example": "2022-12-06T00:13:43.890Z" + "examples": [ + "2022-12-06T00:13:43.890Z" + ] }, "status": { "type": "string", - "example": "ok" + "examples": [ + "ok" + ] } } }, "id": { "type": "string", "description": "The identifier for the rule.", - "example": "b530fed0-74f5-11ed-9801-35303b735aef" + "examples": [ + "b530fed0-74f5-11ed-9801-35303b735aef" + ] }, "last_run": { "type": "object", @@ -6393,51 +6562,71 @@ }, "outcome": { "type": "string", - "example": "succeeded" + "examples": [ + "succeeded" + ] }, "outcome_msg": { - "type": "array", + "type": [ + "array", + "null" + ], "items": { "type": "string" - }, - "nullable": true + } }, "outcome_order": { "type": "integer" }, "warning": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] } } }, "muted_alert_ids": { - "type": "array", - "nullable": true, + "type": [ + "array", + "null" + ], "items": { "type": "string" } }, "mute_all": { "type": "boolean", - "example": false + "examples": [ + false + ] }, "name": { "type": "string", "description": "The name of the rule.", - "example": "cluster_health_rule" + "examples": [ + "cluster_health_rule" + ] }, "next_run": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-12-06T00:14:43.818Z" + "examples": [ + "2022-12-06T00:14:43.818Z" + ] }, "notify_when": { - "type": "string", - "description": "Indicates how often alerts generate actions.", - "nullable": true + "type": [ + "string", + "null" + ], + "description": "Indicates how often alerts generate actions." }, "params": { "type": "object", @@ -6451,7 +6640,9 @@ "rule_type_id": { "type": "string", "description": "The identifier for the type of rule. For example, `.es-query`, `.index-threshold`, `logs.alert.document.count`, `monitoring_alert_cluster_health`, `siem.thresholdRule`, or `xpack.ml.anomaly_detection_alert`.\n", - "example": "monitoring_alert_cluster_health" + "examples": [ + "monitoring_alert_cluster_health" + ] }, "running": { "type": "boolean", @@ -6462,7 +6653,9 @@ }, "scheduled_task_id": { "type": "string", - "example": "b530fed0-74f5-11ed-9801-35303b735aef" + "examples": [ + "b530fed0-74f5-11ed-9801-35303b735aef" + ] }, "tags": { "$ref": "#/components/schemas/tags" @@ -6473,13 +6666,19 @@ "updated_at": { "type": "string", "description": "The date and time that the rule was updated most recently.", - "example": "2022-12-05T23:36:58.284Z" + "examples": [ + "2022-12-05T23:36:58.284Z" + ] }, "updated_by": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "The identifier for the user that updated this rule most recently.", - "nullable": true, - "example": "elastic" + "examples": [ + "elastic" + ] } } }, @@ -6489,7 +6688,9 @@ "properties": { "error": { "type": "string", - "example": "Unauthorized", + "examples": [ + "Unauthorized" + ], "enum": [ "Unauthorized" ] @@ -6499,7 +6700,9 @@ }, "statusCode": { "type": "integer", - "example": 401, + "examples": [ + 401 + ], "enum": [ 401 ] @@ -6511,18 +6714,24 @@ "properties": { "error": { "type": "string", - "example": "Not Found", + "examples": [ + "Not Found" + ], "enum": [ "Not Found" ] }, "message": { "type": "string", - "example": "Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found" + "examples": [ + "Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found" + ] }, "statusCode": { "type": "integer", - "example": 404, + "examples": [ + 404 + ], "enum": [ 404 ] @@ -6545,7 +6754,9 @@ "name": { "type": "string", "description": "The name of the rule.", - "example": "cluster_health_rule" + "examples": [ + "cluster_health_rule" + ] }, "notify_when": { "$ref": "#/components/schemas/notify_when" @@ -6604,28 +6815,40 @@ }, "alertTypeId": { "type": "string", - "example": ".index-threshold" + "examples": [ + ".index-threshold" + ] }, "apiKeyOwner": { - "type": "string", - "nullable": true, - "example": "elastic" + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] }, "createdAt": { "type": "string", "description": "The date and time that the alert was created.", "format": "date-time", - "example": "2022-12-05T23:36:58.284Z" + "examples": [ + "2022-12-05T23:36:58.284Z" + ] }, "createdBy": { "type": "string", "description": "The identifier for the user that created the alert.", - "example": "elastic" + "examples": [ + "elastic" + ] }, "enabled": { "type": "boolean", "description": "Indicates whether the alert is currently enabled.", - "example": true + "examples": [ + true + ] }, "executionStatus": { "type": "object", @@ -6633,26 +6856,36 @@ "lastExecutionDate": { "type": "string", "format": "date-time", - "example": "2022-12-06T00:13:43.890Z" + "examples": [ + "2022-12-06T00:13:43.890Z" + ] }, "status": { "type": "string", - "example": "ok" + "examples": [ + "ok" + ] } } }, "id": { "type": "string", "description": "The identifier for the alert.", - "example": "b530fed0-74f5-11ed-9801-35303b735aef" + "examples": [ + "b530fed0-74f5-11ed-9801-35303b735aef" + ] }, "muteAll": { "type": "boolean", - "example": false + "examples": [ + false + ] }, "mutedInstanceIds": { - "type": "array", - "nullable": true, + "type": [ + "array", + "null" + ], "items": { "type": "string" } @@ -6660,11 +6893,15 @@ "name": { "type": "string", "description": "The name of the alert.", - "example": "my alert" + "examples": [ + "my alert" + ] }, "notifyWhen": { "type": "string", - "example": "onActionGroupChange" + "examples": [ + "onActionGroupChange" + ] }, "params": { "type": "object", @@ -6680,7 +6917,9 @@ }, "scheduledTaskId": { "type": "string", - "example": "b530fed0-74f5-11ed-9801-35303b735aef" + "examples": [ + "b530fed0-74f5-11ed-9801-35303b735aef" + ] }, "tags": { "type": "array", @@ -6689,18 +6928,26 @@ } }, "throttle": { - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "updatedAt": { "type": "string", - "example": "2022-12-05T23:36:58.284Z" + "examples": [ + "2022-12-05T23:36:58.284Z" + ] }, "updatedBy": { - "type": "string", + "type": [ + "string", + "null" + ], "description": "The identifier for the user that updated this alert most recently.", - "nullable": true, - "example": "elastic" + "examples": [ + "elastic" + ] } } } diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.yaml b/x-pack/plugins/alerting/docs/openapi/bundled.yaml index 424326e12c8f6..83fec9274220a 100644 --- a/x-pack/plugins/alerting/docs/openapi/bundled.yaml +++ b/x-pack/plugins/alerting/docs/openapi/bundled.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Alerting description: OpenAPI schema for alerting endpoints - version: '0.1' + version: '0.2' contact: name: Alerting Team license: @@ -147,7 +147,8 @@ paths: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: @@ -303,7 +304,8 @@ paths: schema: type: string default: OR - example: OR + examples: + - AND - name: fields in: query description: The fields to return in the `attributes` key of the response. @@ -333,25 +335,31 @@ paths: schema: type: integer default: 1 - example: 1 + examples: + - 1 - name: per_page in: query description: The number of rules to return per page. schema: type: integer default: 20 - example: 20 + examples: + - 20 - name: search in: query description: An Elasticsearch simple_query_string query that filters the objects in the response. schema: type: string + examples: + - threshold +-test* - name: search_fields in: query description: The fields to perform the simple_query_string parsed query against. schema: oneOf: - type: string + examples: + - name - type: array items: type: string @@ -370,7 +378,8 @@ paths: - asc - desc default: desc - example: asc + examples: + - asc responses: '200': description: Indicates a successful call. @@ -429,7 +438,8 @@ paths: properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -437,14 +447,16 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' execution_health: type: object description: The timestamp and status of the rule run. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -452,14 +464,16 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' read_health: type: object description: The timestamp and status of the rule reading events. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -467,15 +481,18 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' has_permanent_encryption_key: type: boolean description: If `false`, the encrypted saved object plugin does not have a permanent encryption key. - example: true + examples: + - true is_sufficiently_secure: type: boolean description: If `false`, security is enabled but TLS is not. - example: true + examples: + - true examples: getAlertingHealthResponse: $ref: '#/components/examples/get_health_response' @@ -662,14 +679,16 @@ paths: minimum_license_required: description: The subscriptions required to use the rule type. type: string - example: basic + examples: + - basic name: description: The descriptive name of the rule type. type: string producer: description: An identifier for the application that produces this rule type. type: string - example: stackAlerts + examples: + - stackAlerts recovery_action_group: description: An action group to use when an alert goes from an active state to an inactive one. type: object @@ -680,7 +699,8 @@ paths: type: string rule_task_timeout: type: string - example: 5m + examples: + - 5m examples: getRuleTypesResponse: $ref: '#/components/examples/get_rule_types_response' @@ -814,7 +834,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -839,7 +860,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '200': description: Indicates a successful call. @@ -868,7 +890,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - $ref: '#/components/parameters/space_id' requestBody: required: true @@ -939,7 +962,8 @@ paths: interval: type: string description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute. - example: 10s + examples: + - 10s tags: type: array items: @@ -978,7 +1002,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 requestBody: required: true content: @@ -1037,7 +1062,8 @@ paths: interval: type: string description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute. - example: 1d + examples: + - 1d tags: type: array items: @@ -1077,7 +1103,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -1104,7 +1131,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -1131,7 +1159,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -1158,7 +1187,8 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -1185,7 +1215,8 @@ paths: schema: type: string default: OR - example: OR + examples: + - OR - name: fields in: query description: The fields to return in the `attributes` key of the response. @@ -1215,14 +1246,16 @@ paths: schema: type: integer default: 1 - example: 1 + examples: + - 1 - name: per_page in: query description: The number of alerts to return per page. schema: type: integer default: 20 - example: 20 + examples: + - 20 - name: search in: query description: An Elasticsearch `simple_query_string` query that filters the alerts in the response. @@ -1252,7 +1285,8 @@ paths: - asc - desc default: desc - example: asc + examples: + - asc responses: '200': description: Indicates a successful call. @@ -1306,7 +1340,8 @@ paths: properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -1314,14 +1349,16 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' executionHealth: type: object description: The timestamp and status of the alert execution. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -1329,14 +1366,16 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' readHealth: type: object description: The timestamp and status of the alert reading events. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -1344,15 +1383,18 @@ paths: timestamp: type: string format: date-time - example: '2023-01-13T01:28:00.280Z' + examples: + - '2023-01-13T01:28:00.280Z' hasPermanentEncryptionKey: type: boolean description: If `false`, the encrypted saved object plugin does not have a permanent encryption key. - example: true + examples: + - true isSufficientlySecure: type: boolean description: If `false`, security is enabled but TLS is not. - example: true + examples: + - true '401': description: Authorization information is missing or invalid. content: @@ -1478,14 +1520,16 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - in: path name: alertInstanceId description: An identifier for the alert instance. required: true schema: type: string - example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 + examples: + - dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 responses: '204': description: Indicates a successful call. @@ -1512,14 +1556,16 @@ paths: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - in: path name: alertInstanceId description: An identifier for the alert instance. required: true schema: type: string - example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 + examples: + - dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 responses: '204': description: Indicates a successful call. @@ -1537,7 +1583,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' parameters: kbn_xsrf: schema: @@ -1553,7 +1600,8 @@ components: required: true schema: type: string - example: default + examples: + - default rule_id: in: path name: ruleId @@ -1561,7 +1609,8 @@ components: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 alert_id: in: path name: alertId @@ -1569,7 +1618,8 @@ components: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 schemas: filter: type: object @@ -1579,8 +1629,9 @@ components: type: object properties: alias: - type: string - nullable: true + type: + - string + - 'null' controlledBy: type: string disabled: @@ -1615,18 +1666,22 @@ components: - onActionGroupChange - onActiveAlert - onThrottleInterval - example: onActiveAlert + examples: + - onActiveAlert throttle: - type: string + type: + - string + - 'null' description: | The throttle interval, which defines how often an alert generates repeated actions. It is specified in seconds, minutes, hours, or days and is applicable only if `notify_when` is set to `onThrottleInterval`. NOTE: You cannot specify the throttle interval at both the rule and action level. The recommended method is to set it for each action. If you set it at the rule level then update the rule in Kibana, it is automatically changed to use action-specific values. - nullable: true default: null - example: 10m + examples: + - 10m actions: - type: array + type: + - array + - 'null' default: [] - nullable: true items: type: object required: @@ -1661,12 +1716,12 @@ components: description: Defines the days of the week that the action can run, represented as an array of numbers. For example, `1` represents Monday. An empty array is equivalent to specifying all the days of the week. items: type: integer - example: - - 1 - - 2 - - 3 - - 4 - - 5 + examples: + - - 1 + - 2 + - 3 + - 4 + - 5 hours: type: object description: | @@ -1675,20 +1730,24 @@ components: end: type: string description: The end of the time frame in 24-hour notation (`hh:mm`). - example: '17:00' + examples: + - '17:00' start: type: string description: The start of the time frame in 24-hour notation (`hh:mm`). - example: '08:00' + examples: + - '08:00' timezone: type: string description: | The ISO time zone for the `hours` values. Values such as `UTC` and `UTC+1` also work but lack built-in daylight savings time support and are not recommended. - example: Europe/Madrid + examples: + - Europe/Madrid connector_type_id: type: string description: The type of connector. This property appears in responses but cannot be set in requests. - example: .server-log + examples: + - .server-log readOnly: true frequency: type: object @@ -1709,11 +1768,13 @@ components: type: string description: | The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to `default`. - example: default + examples: + - default id: type: string description: The identifier for the connector saved object. - example: 9dca3e00-74f5-11ed-9801-35303b735aef + examples: + - 9dca3e00-74f5-11ed-9801-35303b735aef params: type: object description: The parameters for the action, which are sent to the connector. The `params` are handled as Mustache templates and passed a default set of context. @@ -1721,7 +1782,8 @@ components: uuid: type: string description: A universally unique identifier (UUID) for the action. - example: 1c7a1280-f28c-4e06-96b2-e4e5f05d1d61 + examples: + - 1c7a1280-f28c-4e06-96b2-e4e5f05d1d61 consumer: type: string description: | @@ -1739,7 +1801,8 @@ components: properties: interval: type: string - example: 1m + examples: + - 1m tags: type: array description: The tags for the rule. @@ -2170,7 +2233,8 @@ components: description: | The size of the time window (in `timeWindowUnit` units), which determines how far back to search for documents. Generally it should be a value higher than the rule check interval to avoid gaps in detection. type: integer - example: 5 + examples: + - 5 timewindowunit: description: | The type of units for the time window: seconds, minutes, hours, or days. @@ -2180,7 +2244,8 @@ components: - m - h - d - example: m + examples: + - m size: description: | The number of documents to pass to the configured actions when the threshold condition is met. @@ -2200,7 +2265,8 @@ components: type: array items: type: integer - example: 4000 + examples: + - 4000 thresholdcomparator: description: The comparison function for the threshold. For example, "is above", "is above or equals", "is below", "is below or equals", "is between", and "is not between". type: string @@ -2211,7 +2277,8 @@ components: - <= - between - notBetween - example: '>' + examples: + - '>' params_es_query_rule: oneOf: - type: object @@ -2248,12 +2315,14 @@ components: type: string enum: - esqlQuery - example: esqlQuery + examples: + - esqlQuery size: type: integer description: | When `searchType` is `esqlQuery`, this property is required but it does not affect the rule behavior. - example: 0 + examples: + - 0 termSize: $ref: '#/components/schemas/termsize' threshold: @@ -2270,7 +2339,8 @@ components: The comparison function for the threshold. When `searchType` is `esqlQuery`, this property is required and must be set to ">". Since the `threshold` value must be `0`, the result is that an alert occurs whenever the query returns results. enum: - '>' - example: '>' + examples: + - '>' timeField: $ref: '#/components/schemas/timefield' timeWindowSize: @@ -2315,7 +2385,8 @@ components: properties: language: type: string - example: kuery + examples: + - kuery query: type: string searchType: @@ -2323,7 +2394,8 @@ components: type: string enum: - searchSource - example: searchSource + examples: + - searchSource size: $ref: '#/components/schemas/size' termField: @@ -2375,7 +2447,8 @@ components: enum: - esQuery default: esQuery - example: esQuery + examples: + - esQuery size: $ref: '#/components/schemas/size' termField: @@ -4357,48 +4430,60 @@ components: api_key_created_by_user: type: boolean description: Indicates whether the API key that is associated with the rule was created by the user. - example: false + examples: + - false api_key_owner: - type: string + type: + - string + - 'null' description: | The owner of the API key that is associated with the rule and used to run background tasks. - nullable: true - example: elastic + examples: + - elastic consumer: type: string description: The application or feature that owns the rule. For example, `alerts`, `apm`, `discover`, `infrastructure`, `logs`, `metrics`, `ml`, `monitoring`, `securitySolution`, `siem`, `stackAlerts`, or `uptime`. - example: alerts + examples: + - alerts created_at: type: string description: The date and time that the rule was created. format: date-time - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' created_by: - type: string + type: + - string + - 'null' description: The identifier for the user that created the rule. - nullable: true - example: elastic + examples: + - elastic enabled: type: boolean description: Indicates whether the rule is currently enabled. - example: true + examples: + - true execution_status: type: object properties: last_duration: type: integer - example: 55 + examples: + - 55 last_execution_date: type: string format: date-time - example: '2022-12-06T00:13:43.890Z' + examples: + - '2022-12-06T00:13:43.890Z' status: type: string - example: ok + examples: + - ok id: type: string description: The identifier for the rule. - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef last_run: type: object properties: @@ -4415,39 +4500,49 @@ components: type: integer outcome: type: string - example: succeeded + examples: + - succeeded outcome_msg: - type: array + type: + - array + - 'null' items: type: string - nullable: true outcome_order: type: integer warning: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null muted_alert_ids: - type: array - nullable: true + type: + - array + - 'null' items: type: string mute_all: type: boolean - example: false + examples: + - false name: type: string description: The name of the rule. - example: cluster_health_rule + examples: + - cluster_health_rule next_run: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-12-06T00:14:43.818Z' + examples: + - '2022-12-06T00:14:43.818Z' notify_when: - type: string + type: + - string + - 'null' description: Indicates how often alerts generate actions. - nullable: true params: type: object description: The parameters for the rule. @@ -4459,7 +4554,8 @@ components: type: string description: | The identifier for the type of rule. For example, `.es-query`, `.index-threshold`, `logs.alert.document.count`, `monitoring_alert_cluster_health`, `siem.thresholdRule`, or `xpack.ml.anomaly_detection_alert`. - example: monitoring_alert_cluster_health + examples: + - monitoring_alert_cluster_health running: type: boolean description: Indicates whether the rule is running. @@ -4467,7 +4563,8 @@ components: $ref: '#/components/schemas/schedule' scheduled_task_id: type: string - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef tags: $ref: '#/components/schemas/tags' throttle: @@ -4475,26 +4572,31 @@ components: updated_at: type: string description: The date and time that the rule was updated most recently. - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' updated_by: - type: string + type: + - string + - 'null' description: The identifier for the user that updated this rule most recently. - nullable: true - example: elastic + examples: + - elastic 401_response: type: object title: Unsuccessful rule API response properties: error: type: string - example: Unauthorized + examples: + - Unauthorized enum: - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 enum: - 401 404_response: @@ -4502,15 +4604,18 @@ components: properties: error: type: string - example: Not Found + examples: + - Not Found enum: - Not Found message: type: string - example: Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found + examples: + - Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found statusCode: type: integer - example: 404 + examples: + - 404 enum: - 404 update_rule_request: @@ -4528,7 +4633,8 @@ components: name: type: string description: The name of the rule. - example: cluster_health_rule + examples: + - cluster_health_rule notify_when: $ref: '#/components/schemas/notify_when' params: @@ -4569,53 +4675,66 @@ components: type: object alertTypeId: type: string - example: .index-threshold + examples: + - .index-threshold apiKeyOwner: - type: string - nullable: true - example: elastic + type: + - string + - 'null' + examples: + - elastic createdAt: type: string description: The date and time that the alert was created. format: date-time - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' createdBy: type: string description: The identifier for the user that created the alert. - example: elastic + examples: + - elastic enabled: type: boolean description: Indicates whether the alert is currently enabled. - example: true + examples: + - true executionStatus: type: object properties: lastExecutionDate: type: string format: date-time - example: '2022-12-06T00:13:43.890Z' + examples: + - '2022-12-06T00:13:43.890Z' status: type: string - example: ok + examples: + - ok id: type: string description: The identifier for the alert. - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef muteAll: type: boolean - example: false + examples: + - false mutedInstanceIds: - type: array - nullable: true + type: + - array + - 'null' items: type: string name: type: string description: The name of the alert. - example: my alert + examples: + - my alert notifyWhen: type: string - example: onActionGroupChange + examples: + - onActionGroupChange params: type: object additionalProperties: true @@ -4626,22 +4745,27 @@ components: type: string scheduledTaskId: type: string - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef tags: type: array items: type: string throttle: - type: string - nullable: true + type: + - string + - 'null' updatedAt: type: string - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' updatedBy: - type: string + type: + - string + - 'null' description: The identifier for the user that updated this alert most recently. - nullable: true - example: elastic + examples: + - elastic examples: create_es_query_esql_rule_request: summary: Create an Elasticsearch query rule that uses Elasticsearch Query Language (ES|QL). diff --git a/x-pack/plugins/alerting/docs/openapi/components/parameters/alert_id.yaml b/x-pack/plugins/alerting/docs/openapi/components/parameters/alert_id.yaml index 27427e1ec6758..3ae77530b04d5 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/parameters/alert_id.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/parameters/alert_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the alert. The identifier is generated by the rul required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 \ No newline at end of file + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/parameters/rule_id.yaml b/x-pack/plugins/alerting/docs/openapi/components/parameters/rule_id.yaml index 4b5d14e207353..18cadc0e5d7e6 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/parameters/rule_id.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/parameters/rule_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the rule. required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 \ No newline at end of file + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/parameters/space_id.yaml b/x-pack/plugins/alerting/docs/openapi/components/parameters/space_id.yaml index 0a9fba457e3e7..45787e844caec 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/parameters/space_id.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/parameters/space_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the space. If `/s/` and the identifier are omitte required: true schema: type: string - example: default + examples: + - default diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/401_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/401_response.yaml index c6044998f8649..ed5e2a823bc0f 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/401_response.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/401_response.yaml @@ -3,13 +3,15 @@ title: Unsuccessful rule API response properties: error: type: string - example: Unauthorized + examples: + - Unauthorized enum: - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 enum: - 401 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/404_response.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/404_response.yaml index 1b8a118703ecb..875307d5a3893 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/404_response.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/404_response.yaml @@ -2,14 +2,17 @@ type: object properties: error: type: string - example: Not Found + examples: + - Not Found enum: - Not Found message: type: string - example: "Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found" + examples: + - "Saved object [alert/caaad6d0-920c-11ed-b36a-874bd1548a00] not found" statusCode: type: integer - example: 404 + examples: + - 404 enum: - 404 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml index ee11bead01350..4297900d7a95f 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/actions.yaml @@ -1,6 +1,7 @@ -type: array +type: + - "array" + - "null" default: [] -nullable: true items: type: object required: @@ -37,7 +38,8 @@ items: description: Defines the days of the week that the action can run, represented as an array of numbers. For example, `1` represents Monday. An empty array is equivalent to specifying all the days of the week. items: type: integer - example: [1,2,3,4,5] + examples: + - [1,2,3,4,5] hours: type: object description: > @@ -47,21 +49,25 @@ items: end: type: string description: The end of the time frame in 24-hour notation (`hh:mm`). - example: 17:00 + examples: + - 17:00 start: type: string description: The start of the time frame in 24-hour notation (`hh:mm`). - example: 08:00 + examples: + - 08:00 timezone: type: string description: > The ISO time zone for the `hours` values. Values such as `UTC` and `UTC+1` also work but lack built-in daylight savings time support and are not recommended. - example: Europe/Madrid + examples: + - Europe/Madrid connector_type_id: type: string description: The type of connector. This property appears in responses but cannot be set in requests. - example: .server-log + examples: + - .server-log readOnly: true frequency: type: object @@ -87,11 +93,13 @@ items: The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names. If you don't need to group actions, set to `default`. - example: default + examples: + - default id: type: string description: The identifier for the connector saved object. - example: 9dca3e00-74f5-11ed-9801-35303b735aef + examples: + - 9dca3e00-74f5-11ed-9801-35303b735aef params: type: object description: The parameters for the action, which are sent to the connector. The `params` are handled as Mustache templates and passed a default set of context. @@ -99,4 +107,5 @@ items: uuid: type: string description: A universally unique identifier (UUID) for the action. - example: 1c7a1280-f28c-4e06-96b2-e4e5f05d1d61 + examples: + - 1c7a1280-f28c-4e06-96b2-e4e5f05d1d61 diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml index 06fa627311e75..744e4ead6c94d 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml @@ -7,53 +7,66 @@ properties: type: object alertTypeId: type: string - example: ".index-threshold" + examples: + - ".index-threshold" apiKeyOwner: - type: string - nullable: true - example: elastic + type: + - "string" + - "null" + examples: + - elastic createdAt: type: string description: The date and time that the alert was created. format: date-time - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' createdBy: type: string description: The identifier for the user that created the alert. - example: elastic + examples: + - elastic enabled: type: boolean description: Indicates whether the alert is currently enabled. - example: true + examples: + - true executionStatus: type: object properties: lastExecutionDate: type: string format: date-time - example: '2022-12-06T00:13:43.890Z' + examples: + - '2022-12-06T00:13:43.890Z' status: type: string - example: ok + examples: + - ok id: type: string description: The identifier for the alert. - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef muteAll: type: boolean - example: false + examples: + - false mutedInstanceIds: - type: array - nullable: true + type: + - "array" + - "null" items: type: string name: type: string description: The name of the alert. - example: my alert + examples: + - my alert notifyWhen: type: string - example: onActionGroupChange + examples: + - onActionGroupChange params: type: object additionalProperties: true @@ -64,19 +77,24 @@ properties: type: string scheduledTaskId: type: string - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef tags: type: array items: type: string throttle: - type: string - nullable: true + type: + - "string" + - "null" updatedAt: type: string - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' updatedBy: - type: string + type: + - "string" + - "null" description: The identifier for the user that updated this alert most recently. - nullable: true - example: elastic \ No newline at end of file + examples: + - elastic \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/filter.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/filter.yaml index cb6a77c215682..ba8507f87f051 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/filter.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/filter.yaml @@ -5,8 +5,9 @@ properties: type: object properties: alias: - type: string - nullable: true + type: + - "string" + - "null" controlledBy: type: string disabled: diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/notify_when.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/notify_when.yaml index 0dcd244fc33e4..bb68967b6f5cb 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/notify_when.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/notify_when.yaml @@ -8,4 +8,5 @@ enum: - onActionGroupChange - onActiveAlert - onThrottleInterval -example: onActiveAlert \ No newline at end of file +examples: + - onActiveAlert \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/params_es_query_rule.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/params_es_query_rule.yaml index 99b6a6f393a32..f51cbce979ab2 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/params_es_query_rule.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/params_es_query_rule.yaml @@ -35,12 +35,14 @@ oneOf: type: string enum: - esqlQuery - example: esqlQuery + examples: + - esqlQuery size: type: integer description: > When `searchType` is `esqlQuery`, this property is required but it does not affect the rule behavior. - example: 0 + examples: + - 0 termSize: $ref: 'termsize.yaml' threshold: @@ -60,7 +62,8 @@ oneOf: Since the `threshold` value must be `0`, the result is that an alert occurs whenever the query returns results. enum: - ">" - example: ">" + examples: + - ">" timeField: $ref: 'timefield.yaml' timeWindowSize: @@ -105,7 +108,8 @@ oneOf: properties: language: type: string - example: kuery + examples: + - kuery query: type: string searchType: @@ -113,7 +117,8 @@ oneOf: type: string enum: - searchSource - example: searchSource + examples: + - searchSource size: $ref: 'size.yaml' termField: @@ -165,7 +170,8 @@ oneOf: enum: - esQuery default: esQuery - example: esQuery + examples: + - esQuery size: $ref: 'size.yaml' termField: diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/rule_response_properties.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/rule_response_properties.yaml index 13f62ba63e229..51310c87b6c5c 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/rule_response_properties.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/rule_response_properties.yaml @@ -25,48 +25,60 @@ properties: api_key_created_by_user: type: boolean description: Indicates whether the API key that is associated with the rule was created by the user. - example: false + examples: + - false api_key_owner: - type: string + type: + - "string" + - "null" description: > The owner of the API key that is associated with the rule and used to run background tasks. - nullable: true - example: elastic + examples: + - elastic consumer: type: string description: The application or feature that owns the rule. For example, `alerts`, `apm`, `discover`, `infrastructure`, `logs`, `metrics`, `ml`, `monitoring`, `securitySolution`, `siem`, `stackAlerts`, or `uptime`. - example: alerts + examples: + - alerts created_at: type: string description: The date and time that the rule was created. format: date-time - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' created_by: - type: string + type: + - "string" + - "null" description: The identifier for the user that created the rule. - nullable: true - example: elastic + examples: + - elastic enabled: type: boolean description: Indicates whether the rule is currently enabled. - example: true + examples: + - true execution_status: type: object properties: last_duration: type: integer - example: 55 + examples: + - 55 last_execution_date: type: string format: date-time - example: '2022-12-06T00:13:43.890Z' + examples: + - '2022-12-06T00:13:43.890Z' status: type: string - example: ok + examples: + - ok id: type: string description: The identifier for the rule. - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef last_run: type: object properties: @@ -83,39 +95,49 @@ properties: type: integer outcome: type: string - example: succeeded + examples: + - succeeded outcome_msg: - type: array + type: + - "array" + - "null" items: type: string - nullable: true outcome_order: type: integer warning: - type: string - nullable: true - example: null + type: + - "string" + - "null" + examples: + - null muted_alert_ids: - type: array - nullable: true + type: + - "array" + - "null" items: type: string mute_all: type: boolean - example: false + examples: + - false name: type: string description: The name of the rule. - example: cluster_health_rule + examples: + - cluster_health_rule next_run: - type: string + type: + - "string" + - "null" format: date-time - nullable: true - example: '2022-12-06T00:14:43.818Z' + examples: + - '2022-12-06T00:14:43.818Z' notify_when: - type: string + type: + - "string" + - "null" description: Indicates how often alerts generate actions. - nullable: true params: type: object description: The parameters for the rule. @@ -127,7 +149,8 @@ properties: type: string description: > The identifier for the type of rule. For example, `.es-query`, `.index-threshold`, `logs.alert.document.count`, `monitoring_alert_cluster_health`, `siem.thresholdRule`, or `xpack.ml.anomaly_detection_alert`. - example: monitoring_alert_cluster_health + examples: + - monitoring_alert_cluster_health running: type: boolean description: Indicates whether the rule is running. @@ -135,7 +158,8 @@ properties: $ref: 'schedule.yaml' scheduled_task_id: type: string - example: b530fed0-74f5-11ed-9801-35303b735aef + examples: + - b530fed0-74f5-11ed-9801-35303b735aef tags: $ref: 'tags.yaml' throttle: @@ -143,9 +167,12 @@ properties: updated_at: type: string description: The date and time that the rule was updated most recently. - example: '2022-12-05T23:36:58.284Z' + examples: + - '2022-12-05T23:36:58.284Z' updated_by: - type: string + type: + - "string" + - "null" description: The identifier for the user that updated this rule most recently. - nullable: true - example: elastic \ No newline at end of file + examples: + - elastic \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/schedule.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/schedule.yaml index 57ddf84ceb413..75e98d9a8d193 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/schedule.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/schedule.yaml @@ -3,4 +3,5 @@ description: The check interval, which specifies how frequently the rule conditi properties: interval: type: string - example: 1m \ No newline at end of file + examples: + - 1m \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/threshold.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/threshold.yaml index 4d646f637522d..7060ecf4e91a0 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/threshold.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/threshold.yaml @@ -4,4 +4,5 @@ description: > type: array items: type: integer - example: 4000 \ No newline at end of file + examples: + - 4000 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/thresholdcomparator.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/thresholdcomparator.yaml index 3459365ee0a1d..dd39d4c0bdc3a 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/thresholdcomparator.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/thresholdcomparator.yaml @@ -7,4 +7,5 @@ enum: - "<=" - between - notBetween -example: ">" \ No newline at end of file +examples: + - ">" \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/throttle.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/throttle.yaml index 19d8e621cdaf1..f45573dac8abb 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/throttle.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/throttle.yaml @@ -1,9 +1,11 @@ -type: string +type: + - "string" + - "null" description: > The throttle interval, which defines how often an alert generates repeated actions. It is specified in seconds, minutes, hours, or days and is applicable only if `notify_when` is set to `onThrottleInterval`. NOTE: You cannot specify the throttle interval at both the rule and action level. The recommended method is to set it for each action. If you set it at the rule level then update the rule in Kibana, it is automatically changed to use action-specific values. -nullable: true default: null -example: 10m \ No newline at end of file +examples: + - 10m \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowsize.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowsize.yaml index 7271f62f8dac2..137ae56a91672 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowsize.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowsize.yaml @@ -2,4 +2,5 @@ description: > The size of the time window (in `timeWindowUnit` units), which determines how far back to search for documents. Generally it should be a value higher than the rule check interval to avoid gaps in detection. type: integer -example: 5 \ No newline at end of file +examples: + - 5 \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowunit.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowunit.yaml index c0f2d458ae0e8..bc2cbf1bd3e1c 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowunit.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/timewindowunit.yaml @@ -6,4 +6,5 @@ enum: - m - h - d -example: "m" \ No newline at end of file +examples: + - "m" \ No newline at end of file diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/update_rule_request.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/update_rule_request.yaml index 58b6049aa1077..63293f76ca710 100644 --- a/x-pack/plugins/alerting/docs/openapi/components/schemas/update_rule_request.yaml +++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/update_rule_request.yaml @@ -12,7 +12,8 @@ properties: name: type: string description: The name of the rule. - example: cluster_health_rule + examples: + - cluster_health_rule notify_when: $ref: 'notify_when.yaml' params: diff --git a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml index 5b3d70e78069d..39c46ca8513f9 100644 --- a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml +++ b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Alerting description: OpenAPI schema for alerting endpoints - version: '0.1' + version: '0.2' contact: name: Alerting Team license: @@ -68,7 +68,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - apiKeyAuth: [] diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml index 5b856e24ca997..25ae1cb0b9fc6 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@_health.yaml @@ -28,7 +28,8 @@ get: properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -36,14 +37,16 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" execution_health: type: object description: The timestamp and status of the rule run. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -51,14 +54,16 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" read_health: type: object description: The timestamp and status of the rule reading events. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -66,15 +71,18 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" has_permanent_encryption_key: type: boolean description: If `false`, the encrypted saved object plugin does not have a permanent encryption key. - example: true + examples: + - true is_sufficiently_secure: type: boolean description: If `false`, security is enabled but TLS is not. - example: true + examples: + - true examples: getAlertingHealthResponse: $ref: '../components/examples/get_health_response.yaml' diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml index a4d624110014c..96c5973c570c8 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule@{ruleid}.yaml @@ -90,7 +90,8 @@ post: required: true schema: type: string - example: ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + examples: + - ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 requestBody: required: true content: diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule_types.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule_types.yaml index 1dba3e085e2b7..41ec2aeadbe34 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule_types.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rule_types.yaml @@ -186,14 +186,16 @@ get: minimum_license_required: description: The subscriptions required to use the rule type. type: string - example: basic + examples: + - basic name: description: The descriptive name of the rule type. type: string producer: description: An identifier for the application that produces this rule type. type: string - example: stackAlerts + examples: + - stackAlerts recovery_action_group: description: An action group to use when an alert goes from an active state to an inactive one. type: object @@ -204,7 +206,8 @@ get: type: string rule_task_timeout: type: string - example: 5m + examples: + - 5m examples: getRuleTypesResponse: $ref: '../components/examples/get_rule_types_response.yaml' diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rules@_find.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rules@_find.yaml index f512a0660f181..7f5417eec10c8 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rules@_find.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerting@rules@_find.yaml @@ -19,7 +19,8 @@ get: schema: type: string default: OR - example: OR + examples: + - AND - name: fields in: query description: The fields to return in the `attributes` key of the response. @@ -53,25 +54,31 @@ get: schema: type: integer default: 1 - example: 1 + examples: + - 1 - name: per_page in: query description: The number of rules to return per page. schema: type: integer default: 20 - example: 20 + examples: + - 20 - name: search in: query description: An Elasticsearch simple_query_string query that filters the objects in the response. schema: type: string + examples: + - threshold +-test* - name: search_fields in: query description: The fields to perform the simple_query_string parsed query against. schema: oneOf: - type: string + examples: + - name - type: array items: type: string @@ -91,7 +98,8 @@ get: - asc - desc default: desc - example: asc + examples: + - asc responses: '200': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml index 32dcbbf218a34..2cc8acdf5c5ac 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml @@ -17,7 +17,8 @@ get: schema: type: string default: OR - example: OR + examples: + - OR - name: fields in: query description: The fields to return in the `attributes` key of the response. @@ -51,14 +52,16 @@ get: schema: type: integer default: 1 - example: 1 + examples: + - 1 - name: per_page in: query description: The number of alerts to return per page. schema: type: integer default: 20 - example: 20 + examples: + - 20 - name: search in: query description: An Elasticsearch `simple_query_string` query that filters the alerts in the response. @@ -89,7 +92,8 @@ get: - asc - desc default: desc - example: asc + examples: + - asc responses: '200': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml index eba22ac1b4092..593977bd384d3 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml @@ -26,7 +26,8 @@ get: properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -34,14 +35,16 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" executionHealth: type: object description: The timestamp and status of the alert execution. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -49,14 +52,16 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" readHealth: type: object description: The timestamp and status of the alert reading events. properties: status: type: string - example: ok + examples: + - ok enum: - error - ok @@ -64,15 +69,18 @@ get: timestamp: type: string format: date-time - example: "2023-01-13T01:28:00.280Z" + examples: + - "2023-01-13T01:28:00.280Z" hasPermanentEncryptionKey: type: boolean description: If `false`, the encrypted saved object plugin does not have a permanent encryption key. - example: true + examples: + - true isSufficientlySecure: type: boolean description: If `false`, security is enabled but TLS is not. - example: true + examples: + - true '401': description: Authorization information is missing or invalid. content: diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml index 4af612fc150c5..48a1b12984b4c 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml @@ -16,7 +16,8 @@ delete: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. @@ -42,7 +43,8 @@ get: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '200': description: Indicates a successful call. @@ -72,7 +74,8 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - $ref: '../components/parameters/space_id.yaml' requestBody: required: true @@ -146,7 +149,8 @@ post: interval: type: string description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute. - example: "10s" + examples: + - "10s" tags: type: array items: @@ -189,7 +193,8 @@ put: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 requestBody: required: true content: @@ -251,7 +256,8 @@ put: interval: type: string description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute. - example: "1d" + examples: + - "1d" tags: type: array items: diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml index 62fc4de90cae9..a383c71ed0eae 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml @@ -14,7 +14,8 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml index 85ede004c1890..19b782ea3cccb 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml @@ -14,7 +14,8 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml index 6507cf6da6a18..64c28317025a4 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml @@ -14,7 +14,8 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml index 17c48dae2677b..63fa1d2aa50fa 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml @@ -14,7 +14,8 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml index 439445aa29a82..0f2620aff6a1e 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml @@ -14,14 +14,16 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - in: path name: alertInstanceId description: An identifier for the alert instance. required: true schema: type: string - example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 + examples: + - dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml index 3a7fe8dc1d31a..7b1b6abeab392 100644 --- a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml +++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml @@ -14,14 +14,16 @@ post: required: true schema: type: string - example: 41893910-6bca-11eb-9e0d-85d233e3ee35 + examples: + - 41893910-6bca-11eb-9e0d-85d233e3ee35 - in: path name: alertInstanceId description: An identifier for the alert instance. required: true schema: type: string - example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 + examples: + - dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2 responses: '204': description: Indicates a successful call. diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts index 9526f87e260d6..86fe855152a9a 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.test.ts @@ -1133,6 +1133,22 @@ describe('Alerts Client', () => { }, }, }, + { + index: { + _index: '.internal.alerts-test.alerts-default-000001', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'. Preview of field's value: 'we don't want this field value to be echoed'", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, { index: { _index: '.internal.alerts-test.alerts-default-000002', @@ -1164,7 +1180,7 @@ describe('Alerts Client', () => { expect(clusterClient.bulk).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalledWith( - `Error writing alerts: 1 successful, 0 conflicts, 1 errors: Validation Failed: 1: index is missing;2: type is missing;` + `Error writing alerts: 1 successful, 0 conflicts, 2 errors: Validation Failed: 1: index is missing;2: type is missing;; failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'.` ); }); diff --git a/x-pack/plugins/alerting/server/alerts_client/index.ts b/x-pack/plugins/alerting/server/alerts_client/index.ts index 442f8935650f5..a1c0a309e0dc4 100644 --- a/x-pack/plugins/alerting/server/alerts_client/index.ts +++ b/x-pack/plugins/alerting/server/alerts_client/index.ts @@ -8,3 +8,4 @@ export { type LegacyAlertsClientParams, LegacyAlertsClient } from './legacy_alerts_client'; export { AlertsClient } from './alerts_client'; export type { AlertRuleData } from './types'; +export { sanitizeBulkErrorResponse } from './lib'; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts index 3c5ce6e25a1a8..383d8dbb103fb 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/alert_conflict_resolver.ts @@ -23,6 +23,7 @@ import { } from '@kbn/rule-data-utils'; import { zip, get } from 'lodash'; +import { sanitizeBulkErrorResponse } from '../..'; // these fields are the one's we'll refresh from the fresh mget'd docs const REFRESH_FIELDS_ALWAYS = [ALERT_WORKFLOW_STATUS, ALERT_WORKFLOW_TAGS, ALERT_CASE_IDS]; @@ -269,8 +270,9 @@ interface ResponseStatsResult { // generate a summary of the original bulk request attempt, for logging function getResponseStats(bulkResponse: BulkResponse): ResponseStatsResult { + const sanitizedResponse = sanitizeBulkErrorResponse(bulkResponse) as BulkResponse; const stats: ResponseStatsResult = { success: 0, conflicts: 0, errors: 0, messages: [] }; - for (const item of bulkResponse.items) { + for (const item of sanitizedResponse.items) { const op = item.create || item.index || item.update || item.delete; if (op?.error) { if (op?.status === 409 && op === item.index) { diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/index.ts b/x-pack/plugins/alerting/server/alerts_client/lib/index.ts index 7225e87056e4f..6e40e918a8b2c 100644 --- a/x-pack/plugins/alerting/server/alerts_client/lib/index.ts +++ b/x-pack/plugins/alerting/server/alerts_client/lib/index.ts @@ -16,3 +16,4 @@ export { getContinualAlertsQuery, } from './get_summarized_alerts_query'; export { expandFlattenedAlert } from './format_alert'; +export { sanitizeBulkErrorResponse } from './sanitize_bulk_response'; diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.test.ts b/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.test.ts new file mode 100644 index 0000000000000..533bb5b554ae9 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.test.ts @@ -0,0 +1,244 @@ +/* + * Copyright 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 { TransportResult } from '@elastic/elasticsearch'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { sanitizeBulkErrorResponse } from './sanitize_bulk_response'; + +// Using https://www.elastic.co/guide/en/elasticsearch/reference/8.11/docs-bulk.html +describe('sanitizeBulkErrorResponse', () => { + test('should not modify success response', () => { + const responseBody = { + errors: false, + took: 1, + items: [ + { + index: { + _index: 'test', + _id: '1', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 0, + _primary_term: 1, + }, + }, + { + delete: { + _index: 'test', + _id: '2', + _version: 1, + result: 'not_found', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 404, + _seq_no: 1, + _primary_term: 2, + }, + }, + { + create: { + _index: 'test', + _id: '3', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + { + update: { + _index: 'test', + _id: '1', + _version: 2, + result: 'updated', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 200, + _seq_no: 3, + _primary_term: 4, + }, + }, + ], + }; + const transportResponseBody = wrapResponseBody(responseBody); + + expect(sanitizeBulkErrorResponse(responseBody)).toEqual(responseBody); + expect(sanitizeBulkErrorResponse(transportResponseBody)).toEqual(transportResponseBody); + }); + + test('should not modify error response without field preview', () => { + const responseBody = { + took: 486, + errors: true, + items: [ + { + update: { + _index: 'index1', + _id: '5', + status: 404, + error: { + type: 'document_missing_exception', + reason: '[5]: document missing', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + shard: '0', + index: 'index1', + }, + }, + }, + { + delete: { + _index: 'index1', + _id: '6', + status: 404, + error: { + type: 'document_missing_exception', + reason: '[6]: document missing', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + shard: '0', + index: 'index1', + }, + }, + }, + { + create: { + _index: 'test', + _id: '3', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + ], + }; + const transportResponseBody = wrapResponseBody(responseBody); + + expect(sanitizeBulkErrorResponse(responseBody)).toEqual(responseBody); + expect(sanitizeBulkErrorResponse(transportResponseBody)).toEqual(transportResponseBody); + }); + + test('should sanitize error response with field preview', () => { + const responseBody = { + took: 486, + errors: true, + items: [ + { + update: { + _index: 'index1', + _id: '5', + status: 404, + error: { + type: 'document_missing_exception', + reason: '[5]: document missing', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + shard: '0', + index: 'index1', + }, + }, + }, + { + update: { + _index: 'index1', + _id: '6', + status: 404, + error: { + type: 'document_missing_exception', + reason: '[6]: document missing', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + shard: '0', + index: 'index1', + }, + }, + }, + { + create: { + _index: 'index1', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'. Preview of field's value: 'we don't want this field value to be echoed'", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, + ], + }; + const transportResponseBody = wrapResponseBody(responseBody); + + expect(sanitizeBulkErrorResponse(responseBody)).toEqual({ + ...responseBody, + items: [ + responseBody.items[0], + responseBody.items[1], + { + create: { + _index: 'index1', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'.", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, + ], + }); + expect(sanitizeBulkErrorResponse(transportResponseBody)).toEqual({ + ...transportResponseBody, + body: { + ...transportResponseBody.body, + items: [ + transportResponseBody.body.items[0], + transportResponseBody.body.items[1], + { + create: { + _index: 'index1', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'.", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, + ], + }, + }); + }); +}); + +function wrapResponseBody( + body: estypes.BulkResponse, + statusCode: number = 200 +): TransportResult { + return { + body, + statusCode, + headers: {}, + warnings: null, + // @ts-expect-error + meta: {}, + }; +} diff --git a/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.ts b/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.ts new file mode 100644 index 0000000000000..2b6d9f6e3c2c3 --- /dev/null +++ b/x-pack/plugins/alerting/server/alerts_client/lib/sanitize_bulk_response.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 { cloneDeep } from 'lodash'; +import { TransportResult } from '@elastic/elasticsearch'; +import { get } from 'lodash'; +import { set } from '@kbn/safer-lodash-set'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +export const sanitizeBulkErrorResponse = ( + response: TransportResult | estypes.BulkResponse +): TransportResult | estypes.BulkResponse => { + const clonedResponse = cloneDeep(response); + const isTransportResponse = !!(response as TransportResult).body; + + const responseToUse: estypes.BulkResponse = isTransportResponse + ? (clonedResponse as TransportResult).body + : (clonedResponse as estypes.BulkResponse); + + if (responseToUse.errors) { + (responseToUse.items ?? []).forEach( + (item: Partial>) => { + for (const [_, responseItem] of Object.entries(item)) { + const reason: string = get(responseItem, 'error.reason'); + const redactIndex = reason ? reason.indexOf(`Preview of field's value:`) : -1; + if (redactIndex > 1) { + set(responseItem, 'error.reason', reason.substring(0, redactIndex - 1)); + } + } + } + ); + } + + return clonedResponse; +}; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts new file mode 100644 index 0000000000000..bce6890c22f2c --- /dev/null +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const filterStateStore = { + APP_STATE: 'appState', + GLOBAL_STATE: 'globalState', +} as const; + +export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts new file mode 100644 index 0000000000000..bf5a24b6e4399 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/alerts_filter_query_schemas.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { filterStateStore } from '../constants'; + +export const alertsFilterQuerySchema = schema.object({ + kql: schema.string(), + filters: schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + $state: schema.maybe( + schema.object({ + store: schema.oneOf([ + schema.literal(filterStateStore.APP_STATE), + schema.literal(filterStateStore.GLOBAL_STATE), + ]), + }) + ), + }) + ), + dsl: schema.maybe(schema.string()), +}); diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/index.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/index.ts new file mode 100644 index 0000000000000..5042e3e8265a6 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/schemas/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { alertsFilterQuerySchema } from './alerts_filter_query_schemas'; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/types/alerts_filter_query.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/types/alerts_filter_query.ts new file mode 100644 index 0000000000000..926d27e89fa94 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/types/alerts_filter_query.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. + */ + +import { TypeOf } from '@kbn/config-schema'; +import { alertsFilterQuerySchema } from '../schemas/alerts_filter_query_schemas'; + +export type AlertsFilterQuery = TypeOf; diff --git a/x-pack/plugins/alerting/server/application/alerts_filter_query/types/index.ts b/x-pack/plugins/alerting/server/application/alerts_filter_query/types/index.ts new file mode 100644 index 0000000000000..2553fd0005904 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/alerts_filter_query/types/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { AlertsFilterQuery } from './alerts_filter_query'; diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts b/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts index caa1616d10a7c..4b5920d645584 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/constants.ts @@ -13,8 +13,12 @@ export const maintenanceWindowStatus = { } as const; export const maintenanceWindowCategoryIdTypes = { - KIBANA: 'kibana', OBSERVABILITY: 'observability', SECURITY_SOLUTION: 'securitySolution', MANAGEMENT: 'management', } as const; + +export const filterStateStore = { + APP_STATE: 'appState', + GLOBAL_STATE: 'globalState', +} as const; diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/lib/generate_maintenance_window_events.ts b/x-pack/plugins/alerting/server/application/maintenance_window/lib/generate_maintenance_window_events.ts index 99f8e99f3f5e3..78227dab5864a 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/lib/generate_maintenance_window_events.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/lib/generate_maintenance_window_events.ts @@ -8,7 +8,8 @@ import _ from 'lodash'; import moment from 'moment-timezone'; import { RRule, Weekday } from '@kbn/rrule'; -import { RRuleParams, MaintenanceWindowSOAttributes, DateRange } from '../../../../common'; +import { RRuleParams, DateRange } from '../../../../common'; +import { MaintenanceWindow } from '../types'; export interface GenerateMaintenanceWindowEventsParams { rRule: RRuleParams; @@ -58,7 +59,7 @@ export const shouldRegenerateEvents = ({ rRule, duration, }: { - maintenanceWindow: MaintenanceWindowSOAttributes; + maintenanceWindow: MaintenanceWindow; rRule?: RRuleParams; duration?: number; }): boolean => { diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts index 1de657bdfd6ef..af04b62bfc7d9 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.test.ts @@ -133,6 +133,124 @@ describe('MaintenanceWindowClient - create', () => { ); }); + it('should create maintenance window with scoped query', async () => { + jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); + + const mockMaintenanceWindow = getMockMaintenanceWindow({ + expirationDate: moment(new Date()).tz('UTC').add(1, 'year').toISOString(), + }); + + savedObjectsClient.create.mockResolvedValueOnce({ + attributes: mockMaintenanceWindow, + version: '123', + id: 'test-id', + } as unknown as SavedObject); + + await createMaintenanceWindow(mockContext, { + data: { + title: mockMaintenanceWindow.title, + duration: mockMaintenanceWindow.duration, + rRule: mockMaintenanceWindow.rRule as CreateMaintenanceWindowParams['data']['rRule'], + categoryIds: ['observability', 'securitySolution'], + scopedQuery: { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }, + }, + }); + + expect(savedObjectsClient.create).toHaveBeenLastCalledWith( + MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + expect.objectContaining({ + title: mockMaintenanceWindow.title, + duration: mockMaintenanceWindow.duration, + rRule: mockMaintenanceWindow.rRule, + enabled: true, + expirationDate: moment(new Date()).tz('UTC').add(1, 'year').toISOString(), + categoryIds: ['observability', 'securitySolution'], + ...updatedMetadata, + }), + { + id: expect.any(String), + } + ); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.kql + ).toEqual(`_id: '1234'`); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.filters[0] + ).toEqual({ + $state: { store: 'appState' }, + meta: { + alias: null, + disabled: false, + field: 'kibana.alert.action_group', + key: 'kibana.alert.action_group', + negate: false, + params: { query: 'test' }, + type: 'phrase', + }, + query: { match_phrase: { 'kibana.alert.action_group': 'test' } }, + }); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.dsl + ).toMatchInlineSnapshot( + `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"_id\\":\\"'1234'\\"}}],\\"minimum_should_match\\":1}},{\\"match_phrase\\":{\\"kibana.alert.action_group\\":\\"test\\"}}],\\"should\\":[],\\"must_not\\":[]}}"` + ); + }); + + it('should throw if trying to create a maintenance window with invalid scoped query', async () => { + jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); + + const mockMaintenanceWindow = getMockMaintenanceWindow({ + expirationDate: moment(new Date()).tz('UTC').add(1, 'year').toISOString(), + }); + + await expect(async () => { + await createMaintenanceWindow(mockContext, { + data: { + title: mockMaintenanceWindow.title, + duration: mockMaintenanceWindow.duration, + rRule: mockMaintenanceWindow.rRule as CreateMaintenanceWindowParams['data']['rRule'], + categoryIds: ['observability', 'securitySolution'], + scopedQuery: { + kql: 'invalid: ', + filters: [], + }, + }, + }); + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Error validating create maintenance scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. + invalid: + ---------^" + `); + }); + it('should throw if trying to create a maintenance window with invalid category ids', async () => { jest.useFakeTimers().setSystemTime(new Date('2023-02-26T00:00:00.000Z')); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts index b8fba87b2fc18..78f5e885874b0 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/create_maintenance_window.ts @@ -8,6 +8,7 @@ import moment from 'moment'; import Boom from '@hapi/boom'; import { SavedObjectsUtils } from '@kbn/core/server'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import { generateMaintenanceWindowEvents } from '../../lib/generate_maintenance_window_events'; import type { MaintenanceWindowClientContext } from '../../../../../common'; import type { MaintenanceWindow } from '../../types'; @@ -25,7 +26,7 @@ export async function createMaintenanceWindow( ): Promise { const { data } = params; const { savedObjectsClient, getModificationMetadata, logger } = context; - const { title, duration, rRule, categoryIds } = data; + const { title, duration, rRule, categoryIds, scopedQuery } = data; try { createMaintenanceWindowParamsSchema.validate(params); @@ -33,6 +34,25 @@ export async function createMaintenanceWindow( throw Boom.badRequest(`Error validating create maintenance window data - ${error.message}`); } + let scopedQueryWithGeneratedValue = scopedQuery; + try { + if (scopedQuery) { + const dsl = JSON.stringify( + buildEsQuery( + undefined, + [{ query: scopedQuery.kql, language: 'kuery' }], + scopedQuery.filters as Filter[] + ) + ); + scopedQueryWithGeneratedValue = { + ...scopedQuery, + dsl, + }; + } + } catch (error) { + throw Boom.badRequest(`Error validating create maintenance scoped query - ${error.message}`); + } + const id = SavedObjectsUtils.generateId(); const expirationDate = moment().utc().add(1, 'year').toISOString(); const modificationMetadata = await getModificationMetadata(); @@ -43,6 +63,7 @@ export async function createMaintenanceWindow( enabled: true, expirationDate, categoryIds, + scopedQuery: scopedQueryWithGeneratedValue, rRule: rRule as MaintenanceWindow['rRule'], duration, events, diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/schemas/create_maintenance_window_params_schema.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/schemas/create_maintenance_window_params_schema.ts index dcb9150f2385b..b55a4871d0b6f 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/schemas/create_maintenance_window_params_schema.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/create/schemas/create_maintenance_window_params_schema.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowCategoryIdsSchema } from '../../../schemas'; import { rRuleRequestSchema } from '../../../../r_rule/schemas'; +import { alertsFilterQuerySchema } from '../../../../alerts_filter_query/schemas'; export const createMaintenanceWindowParamsSchema = schema.object({ data: schema.object({ @@ -15,5 +16,6 @@ export const createMaintenanceWindowParamsSchema = schema.object({ duration: schema.number(), rRule: rRuleRequestSchema, categoryIds: maintenanceWindowCategoryIdsSchema, + scopedQuery: schema.maybe(schema.nullable(alertsFilterQuerySchema)), }), }); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/schemas/update_maintenance_window_params_schema.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/schemas/update_maintenance_window_params_schema.ts index 84120a5ee8c44..1aca8ab24fcda 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/schemas/update_maintenance_window_params_schema.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/schemas/update_maintenance_window_params_schema.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowCategoryIdsSchema } from '../../../schemas'; import { rRuleRequestSchema } from '../../../../r_rule/schemas'; +import { alertsFilterQuerySchema } from '../../../../alerts_filter_query/schemas'; export const updateMaintenanceWindowParamsSchema = schema.object({ id: schema.string(), @@ -17,5 +18,6 @@ export const updateMaintenanceWindowParamsSchema = schema.object({ duration: schema.maybe(schema.number()), rRule: schema.maybe(rRuleRequestSchema), categoryIds: maintenanceWindowCategoryIdsSchema, + scopedQuery: schema.maybe(schema.nullable(alertsFilterQuerySchema)), }), }); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts index 966808fed57f2..4caef4eca1ba1 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.test.ts @@ -207,6 +207,172 @@ describe('MaintenanceWindowClient - update', () => { ); }); + it('should update maintenance window with scoped query', async () => { + jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); + + const modifiedEvents = [ + { gte: '2023-03-26T00:00:00.000Z', lte: '2023-03-26T00:12:34.000Z' }, + { gte: '2023-04-01T23:00:00.000Z', lte: '2023-04-01T23:43:21.000Z' }, + ]; + const mockMaintenanceWindow = getMockMaintenanceWindow({ + rRule: { + tzid: 'CET', + dtstart: '2023-03-26T00:00:00.000Z', + freq: Frequency.WEEKLY, + count: 5, + } as MaintenanceWindow['rRule'], + events: modifiedEvents, + expirationDate: moment(new Date(firstTimestamp)).tz('UTC').add(2, 'week').toISOString(), + }); + + savedObjectsClient.get.mockResolvedValue({ + attributes: mockMaintenanceWindow, + version: '123', + id: 'test-id', + } as unknown as SavedObject); + + savedObjectsClient.create.mockResolvedValue({ + attributes: { + ...mockMaintenanceWindow, + ...updatedAttributes, + ...updatedMetadata, + }, + id: 'test-id', + } as unknown as SavedObject); + + await updateMaintenanceWindow(mockContext, { + id: 'test-id', + data: { + scopedQuery: { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }, + }, + }); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.kql + ).toEqual(`_id: '1234'`); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.filters[0] + ).toEqual({ + $state: { store: 'appState' }, + meta: { + alias: null, + disabled: false, + field: 'kibana.alert.action_group', + key: 'kibana.alert.action_group', + negate: false, + params: { query: 'test' }, + type: 'phrase', + }, + query: { match_phrase: { 'kibana.alert.action_group': 'test' } }, + }); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery!.dsl + ).toMatchInlineSnapshot( + `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"_id\\":\\"'1234'\\"}}],\\"minimum_should_match\\":1}},{\\"match_phrase\\":{\\"kibana.alert.action_group\\":\\"test\\"}}],\\"should\\":[],\\"must_not\\":[]}}"` + ); + }); + + it('should remove maintenance window with scoped query', async () => { + jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); + + const modifiedEvents = [ + { gte: '2023-03-26T00:00:00.000Z', lte: '2023-03-26T00:12:34.000Z' }, + { gte: '2023-04-01T23:00:00.000Z', lte: '2023-04-01T23:43:21.000Z' }, + ]; + const mockMaintenanceWindow = getMockMaintenanceWindow({ + rRule: { + tzid: 'CET', + dtstart: '2023-03-26T00:00:00.000Z', + freq: Frequency.WEEKLY, + count: 5, + } as MaintenanceWindow['rRule'], + events: modifiedEvents, + expirationDate: moment(new Date(firstTimestamp)).tz('UTC').add(2, 'week').toISOString(), + }); + + savedObjectsClient.get.mockResolvedValue({ + attributes: mockMaintenanceWindow, + version: '123', + id: 'test-id', + } as unknown as SavedObject); + + savedObjectsClient.create.mockResolvedValue({ + attributes: { + ...mockMaintenanceWindow, + ...updatedAttributes, + ...updatedMetadata, + }, + id: 'test-id', + } as unknown as SavedObject); + + await updateMaintenanceWindow(mockContext, { + id: 'test-id', + data: { + scopedQuery: null, + }, + }); + + expect( + (savedObjectsClient.create.mock.calls[0][1] as MaintenanceWindow).scopedQuery + ).toBeNull(); + }); + + it('should throw if updating a maintenance window with invalid scoped query', async () => { + jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); + const mockMaintenanceWindow = getMockMaintenanceWindow({ + expirationDate: moment(new Date(firstTimestamp)).tz('UTC').subtract(1, 'year').toISOString(), + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + attributes: mockMaintenanceWindow, + version: '123', + id: 'test-id', + } as unknown as SavedObject); + + await expect(async () => { + await updateMaintenanceWindow(mockContext, { + id: 'test-id', + data: { + scopedQuery: { + kql: 'invalid: ', + filters: [], + }, + }, + }); + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Error validating update maintenance scoped query - Expected \\"(\\", \\"{\\", value, whitespace but end of input found. + invalid: + ---------^" + `); + }); + it('should throw if updating a maintenance window that has expired', async () => { jest.useFakeTimers().setSystemTime(new Date(firstTimestamp)); const mockMaintenanceWindow = getMockMaintenanceWindow({ diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts index f10381defc9b9..6b7ab69cc8070 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/methods/update/update_maintenance_window.ts @@ -7,6 +7,7 @@ import moment from 'moment'; import Boom from '@hapi/boom'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import type { MaintenanceWindowClientContext } from '../../../../../common'; import type { MaintenanceWindow } from '../../types'; import { @@ -45,7 +46,7 @@ async function updateWithOCC( ): Promise { const { savedObjectsClient, getModificationMetadata, logger } = context; const { id, data } = params; - const { title, enabled, duration, rRule, categoryIds } = data; + const { title, enabled, duration, rRule, categoryIds, scopedQuery } = data; try { updateMaintenanceWindowParamsSchema.validate(params); @@ -53,6 +54,25 @@ async function updateWithOCC( throw Boom.badRequest(`Error validating update maintenance window data - ${error.message}`); } + let scopedQueryWithGeneratedValue = scopedQuery; + try { + if (scopedQuery) { + const dsl = JSON.stringify( + buildEsQuery( + undefined, + [{ query: scopedQuery.kql, language: 'kuery' }], + scopedQuery.filters as Filter[] + ) + ); + scopedQueryWithGeneratedValue = { + ...scopedQuery, + dsl, + }; + } + } catch (error) { + throw Boom.badRequest(`Error validating update maintenance scoped query - ${error.message}`); + } + try { const { attributes, @@ -88,6 +108,9 @@ async function updateWithOCC( ...(title ? { title } : {}), ...(rRule ? { rRule: rRule as MaintenanceWindow['rRule'] } : {}), ...(categoryIds !== undefined ? { categoryIds } : {}), + ...(scopedQueryWithGeneratedValue !== undefined + ? { scopedQuery: scopedQueryWithGeneratedValue } + : {}), ...(typeof duration === 'number' ? { duration } : {}), ...(typeof enabled === 'boolean' ? { enabled } : {}), expirationDate, diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/schemas/maintenance_window_schemas.ts b/x-pack/plugins/alerting/server/application/maintenance_window/schemas/maintenance_window_schemas.ts index 080f5b1bbe676..cf9b69454aa9e 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/schemas/maintenance_window_schemas.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/schemas/maintenance_window_schemas.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import { maintenanceWindowStatus, maintenanceWindowCategoryIdTypes } from '../constants'; import { rRuleSchema } from '../../r_rule/schemas'; +import { alertsFilterQuerySchema } from '../../alerts_filter_query/schemas'; export const maintenanceWindowEventSchema = schema.object({ gte: schema.string(), @@ -47,4 +48,5 @@ export const maintenanceWindowSchema = schema.object({ schema.literal(maintenanceWindowStatus.ARCHIVED), ]), categoryIds: maintenanceWindowCategoryIdsSchema, + scopedQuery: schema.maybe(schema.nullable(alertsFilterQuerySchema)), }); diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_attributes_to_maintenance_window.ts b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_attributes_to_maintenance_window.ts index 53996f29694f4..6a2f25ce18dd9 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_attributes_to_maintenance_window.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_attributes_to_maintenance_window.ts @@ -40,5 +40,6 @@ export const transformMaintenanceWindowAttributesToMaintenanceWindow = ( eventEndTime, status, ...(attributes.categoryIds !== undefined ? { categoryIds: attributes.categoryIds } : {}), + ...(attributes.scopedQuery !== undefined ? { scopedQuery: attributes.scopedQuery } : {}), }; }; diff --git a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts index 35865f38aa9b2..9d26443bdc07d 100644 --- a/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts +++ b/x-pack/plugins/alerting/server/application/maintenance_window/transforms/transform_maintenance_window_to_maintenance_window_attributes.ts @@ -25,5 +25,8 @@ export const transformMaintenanceWindowToMaintenanceWindowAttributes = ( ...(maintenanceWindow.categoryIds !== undefined ? { categoryIds: maintenanceWindow.categoryIds } : {}), + ...(maintenanceWindow.scopedQuery !== undefined + ? { scopedQuery: maintenanceWindow.scopedQuery } + : {}), }; }; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts index 294945ad82037..996f67448c7f8 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts @@ -489,7 +489,7 @@ async function updateRuleAttributesAndParamsInMemory( context, operations, rule: ruleDomain, - ruleActions, + ruleActions: ruleActions as RuleDomain['actions'], // TODO (http-versioning) Remove this cast once we fix injectReferencesIntoActions ruleType, }); diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts index 579f41adb0424..7cbadb6199081 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts @@ -7,31 +7,10 @@ import { schema } from '@kbn/config-schema'; import { notifyWhenSchema } from './notify_when_schema'; -import { filterStateStore } from '../constants'; +import { alertsFilterQuerySchema } from '../../alerts_filter_query/schemas'; export const actionParamsSchema = schema.recordOf(schema.string(), schema.maybe(schema.any())); -const actionAlertsFilterQueryFiltersSchema = schema.arrayOf( - schema.object({ - query: schema.maybe(schema.recordOf(schema.string(), schema.any())), - meta: schema.recordOf(schema.string(), schema.any()), - $state: schema.maybe( - schema.object({ - store: schema.oneOf([ - schema.literal(filterStateStore.APP_STATE), - schema.literal(filterStateStore.GLOBAL_STATE), - ]), - }) - ), - }) -); - -const actionDomainAlertsFilterQuerySchema = schema.object({ - kql: schema.string(), - filters: actionAlertsFilterQueryFiltersSchema, - dsl: schema.maybe(schema.string()), -}); - const actionAlertsFilterTimeFrameSchema = schema.object({ days: schema.arrayOf( schema.oneOf([ @@ -52,7 +31,7 @@ const actionAlertsFilterTimeFrameSchema = schema.object({ }); const actionDomainAlertsFilterSchema = schema.object({ - query: schema.maybe(actionDomainAlertsFilterQuerySchema), + query: schema.maybe(alertsFilterQuerySchema), timeframe: schema.maybe(actionAlertsFilterTimeFrameSchema), }); @@ -76,17 +55,8 @@ export const actionDomainSchema = schema.object({ useAlertDataAsTemplate: schema.maybe(schema.boolean()), }); -/** - * Sanitized (non-domain) action schema, returned by rules clients for other solutions - */ -const actionAlertsFilterQuerySchema = schema.object({ - kql: schema.string(), - filters: actionAlertsFilterQueryFiltersSchema, - dsl: schema.maybe(schema.string()), -}); - export const actionAlertsFilterSchema = schema.object({ - query: schema.maybe(actionAlertsFilterQuerySchema), + query: schema.maybe(alertsFilterQuerySchema), timeframe: schema.maybe(actionAlertsFilterTimeFrameSchema), }); diff --git a/x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts b/x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts new file mode 100644 index 0000000000000..bce6890c22f2c --- /dev/null +++ b/x-pack/plugins/alerting/server/data/alerts_filter_query/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const filterStateStore = { + APP_STATE: 'appState', + GLOBAL_STATE: 'globalState', +} as const; + +export type FilterStateStore = typeof filterStateStore[keyof typeof filterStateStore]; diff --git a/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.ts new file mode 100644 index 0000000000000..f740d8070abf2 --- /dev/null +++ b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/alerts_filter_query_attributes.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 type { FilterStateStore } from '../constants'; + +export interface AlertsFilterAttributes { + query?: Record; + meta: Record; + $state?: { + store: FilterStateStore; + }; +} + +export interface AlertsFilterQueryAttributes { + kql: string; + filters: AlertsFilterAttributes[]; + dsl?: string; +} diff --git a/x-pack/plugins/alerting/server/data/alerts_filter_query/types/index.ts b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/index.ts new file mode 100644 index 0000000000000..6258f925b6e79 --- /dev/null +++ b/x-pack/plugins/alerting/server/data/alerts_filter_query/types/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { AlertsFilterQueryAttributes } from './alerts_filter_query_attributes'; diff --git a/x-pack/plugins/alerting/server/data/maintenance_window/types/maintenance_window_attributes.ts b/x-pack/plugins/alerting/server/data/maintenance_window/types/maintenance_window_attributes.ts index 91f4e95172551..afb217abaf139 100644 --- a/x-pack/plugins/alerting/server/data/maintenance_window/types/maintenance_window_attributes.ts +++ b/x-pack/plugins/alerting/server/data/maintenance_window/types/maintenance_window_attributes.ts @@ -6,7 +6,8 @@ */ import { RRuleAttributes } from '../../r_rule/types'; -import { MaintenanceWindowCategoryIdTypes } from '../constants'; +import type { MaintenanceWindowCategoryIdTypes } from '../constants'; +import { AlertsFilterQueryAttributes } from '../../alerts_filter_query/types'; export interface MaintenanceWindowEventAttributes { gte: string; @@ -25,4 +26,5 @@ export interface MaintenanceWindowAttributes { createdAt: string; updatedAt: string; categoryIds?: MaintenanceWindowCategoryIdTypes[] | null; + scopedQuery?: AlertsFilterQueryAttributes | null; } diff --git a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts index aa8adda873cde..19a669e6bd33e 100644 --- a/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts +++ b/x-pack/plugins/alerting/server/data/rule/types/rule_attributes.ts @@ -6,7 +6,6 @@ */ import type { SavedObjectAttributes } from '@kbn/core/server'; -import { Filter } from '@kbn/es-query'; import { IsoWeekday } from '../../../../common'; import { ruleNotifyWhenAttributes, @@ -16,6 +15,7 @@ import { ruleExecutionStatusWarningReasonAttributes, } from '../constants'; import { RRuleAttributes } from '../../r_rule/types'; +import { AlertsFilterQueryAttributes } from '../../alerts_filter_query/types'; export type RuleNotifyWhenAttributes = typeof ruleNotifyWhenAttributes[keyof typeof ruleNotifyWhenAttributes]; @@ -115,11 +115,7 @@ interface AlertsFilterTimeFrameAttributes { } interface AlertsFilterAttributes { - query?: { - kql: string; - filters: Filter[]; - dsl: string; - }; + query?: AlertsFilterQueryAttributes; timeframe?: AlertsFilterTimeFrameAttributes; } diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index b6962f65b4ab8..13237f92b1c1d 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -67,6 +67,7 @@ export { isValidAlertIndexName, InstallShutdownError, } from './alerts_service'; +export { sanitizeBulkErrorResponse } from './alerts_client'; export { getDataStreamAdapter } from './alerts_service/lib/data_stream_adapter'; export const plugin = async (initContext: PluginInitializerContext) => { diff --git a/x-pack/plugins/alerting/server/lib/create_get_alert_indices_alias.test.ts b/x-pack/plugins/alerting/server/lib/create_get_alert_indices_alias.test.ts index 9a8109977962c..32fac87b7485b 100644 --- a/x-pack/plugins/alerting/server/lib/create_get_alert_indices_alias.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_get_alert_indices_alias.test.ts @@ -33,6 +33,7 @@ describe('createGetAlertIndicesAliasFn', () => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, + latestRuleVersion: 1, }; const registry = new RuleTypeRegistry(ruleTypeRegistryParams); registry.register({ diff --git a/x-pack/plugins/alerting/server/lib/license_state.ts b/x-pack/plugins/alerting/server/lib/license_state.ts index c774673934ebf..99be775f7c3c6 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.ts @@ -13,7 +13,7 @@ import { capitalize } from 'lodash'; import { Observable, Subscription } from 'rxjs'; import { LicensingPluginStart } from '@kbn/licensing-plugin/server'; import { ILicense, LicenseType } from '@kbn/licensing-plugin/common/types'; -import { PLUGIN } from '../constants/plugin'; +import { PLUGIN } from '../../common/constants/plugin'; import { getRuleTypeFeatureUsageName } from './get_rule_type_feature_usage_name'; import { RuleType, diff --git a/x-pack/plugins/alerting/server/maintenance_window_client_factory.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client_factory.test.ts index 9f3f2d26447d1..a1ec0a351729c 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client_factory.test.ts @@ -14,7 +14,7 @@ import { savedObjectsServiceMock, loggingSystemMock, } from '@kbn/core/server/mocks'; -import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE } from '../common'; diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 03431c2804c6e..64bc8ca05281f 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -77,7 +77,7 @@ import { } from './types'; import { registerAlertingUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; -import { setupSavedObjects } from './saved_objects'; +import { setupSavedObjects, getLatestRuleVersion } from './saved_objects'; import { initializeApiKeyInvalidator, scheduleApiKeyInvalidatorTask, @@ -305,6 +305,7 @@ export class AlertingPlugin { alertsService: this.alertsService, minimumScheduleInterval: this.config.rules.minimumScheduleInterval, inMemoryMetrics: this.inMemoryMetrics, + latestRuleVersion: getLatestRuleVersion(), }); this.ruleTypeRegistry = ruleTypeRegistry; diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts index 16309485c18a1..96c2275d113a2 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts @@ -24,7 +24,7 @@ export const rewriteActionsReq = ( }) => { return { ...action, - useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), ...(frequency ? { frequency: { @@ -49,7 +49,9 @@ export const rewriteActionsRes = (actions?: RuleAction[]) => { ({ actionTypeId, frequency, alertsFilter, useAlertDataForTemplate, ...action }) => ({ ...action, connector_type_id: actionTypeId, - use_alert_data_for_template: useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), ...(frequency ? { frequency: rewriteFrequency(frequency) } : {}), ...(alertsFilter ? { diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/create/transforms/transform_create_body/v1.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/create/transforms/transform_create_body/v1.ts index 03627c258c5eb..6eaa77fc78624 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/create/transforms/transform_create_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/create/transforms/transform_create_body/v1.ts @@ -16,5 +16,6 @@ export const transformCreateBody = ( duration: createBody.duration, rRule: createBody.r_rule, categoryIds: createBody.category_ids, + scopedQuery: createBody.scoped_query, }; }; diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/update/transforms/transform_update_body/v1.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/update/transforms/transform_update_body/v1.ts index 9da852bd6ebff..96c4e92d4934e 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/apis/update/transforms/transform_update_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/apis/update/transforms/transform_update_body/v1.ts @@ -11,12 +11,21 @@ import { UpdateMaintenanceWindowParams } from '../../../../../../application/mai export const transformUpdateBody = ( updateBody: UpdateMaintenanceWindowRequestBodyV1 ): UpdateMaintenanceWindowParams['data'] => { - const { title, enabled, duration, r_rule: rRule, category_ids: categoryIds } = updateBody; + const { + title, + enabled, + duration, + r_rule: rRule, + category_ids: categoryIds, + scoped_query: scopedQuery, + } = updateBody; + return { ...(title !== undefined ? { title } : {}), ...(enabled !== undefined ? { enabled } : {}), ...(duration !== undefined ? { duration } : {}), ...(rRule !== undefined ? { rRule } : {}), ...(categoryIds !== undefined ? { categoryIds } : {}), + ...(scopedQuery !== undefined ? { scopedQuery } : {}), }; }; diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/transforms/transform_maintenance_window_to_response/v1.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/transforms/transform_maintenance_window_to_response/v1.ts index e6e0fb9a6aa42..2d21576773c29 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/transforms/transform_maintenance_window_to_response/v1.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/transforms/transform_maintenance_window_to_response/v1.ts @@ -29,5 +29,8 @@ export const transformMaintenanceWindowToResponse = ( ...(maintenanceWindow.categoryIds !== undefined ? { category_ids: maintenanceWindow.categoryIds } : {}), + ...(maintenanceWindow.scopedQuery !== undefined + ? { scoped_query: maintenanceWindow.scopedQuery } + : {}), }; }; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts index 9a77d46fb9208..1b1ed454c5207 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts @@ -114,7 +114,6 @@ describe('bulkEditRulesRoute', () => { foo: true, }, uuid: '123-456', - use_alert_data_for_template: false, }, ], }), diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts index a12b8ee5e177c..e952a72ec3859 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts @@ -124,7 +124,6 @@ describe('createRuleRoute', () => { }, connector_type_id: 'test', uuid: '123-456', - use_alert_data_for_template: false, }, ], }; @@ -199,7 +198,6 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, - "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -316,7 +314,6 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, - "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -434,7 +431,6 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, - "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -552,7 +548,6 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, - "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts index a9e9a25d05c95..97ff8b383e4c5 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts @@ -27,7 +27,7 @@ const transformCreateBodyActions = (actions: CreateRuleActionV1[]): CreateRuleDa id: action.id, params: action.params, actionTypeId: action.actionTypeId, - useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), ...(action.uuid ? { uuid: action.uuid } : {}), ...(frequency ? { diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts index 3968e0e271620..fb6799f2b5e48 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts @@ -63,7 +63,9 @@ export const transformRuleToRuleResponse = ( id, params, connector_type_id: actionTypeId, - use_alert_data_for_template: useAlertDataForTemplate ?? false, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), ...(frequency ? { frequency: { diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index 87901feab64cf..5a4b3a19c0d7c 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -132,7 +132,6 @@ describe('updateRuleRoute', () => { "params": Object { "baz": true, }, - "useAlertDataForTemplate": undefined, "uuid": "1234-5678", }, ], diff --git a/x-pack/plugins/alerting/server/rule_type_registry.mock.ts b/x-pack/plugins/alerting/server/rule_type_registry.mock.ts index 706484fdd92f6..0aa7e5b68b40c 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.mock.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.mock.ts @@ -18,6 +18,7 @@ const createRuleTypeRegistryMock = () => { list: jest.fn(), getAllTypes: jest.fn(), ensureRuleTypeEnabled: jest.fn(), + getLatestRuleVersion: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 8e01e7d05cbd1..2059f1e7548e6 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -40,6 +40,7 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, + latestRuleVersion: 1, }; }); @@ -782,6 +783,7 @@ describe('Create Lifecycle', () => { "defaultScheduleInterval": undefined, "doesSetRecoveryContext": false, "enabledInLicense": false, + "fieldsForAAD": undefined, "hasAlertsMappings": true, "hasFieldsForAAD": false, "id": "test", @@ -911,6 +913,16 @@ describe('Create Lifecycle', () => { ).toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); }); + + describe('getLatestRuleVersion', () => { + test('should return the latest rule version', async () => { + const ruleTypeRegistry = new RuleTypeRegistry({ + ...ruleTypeRegistryParams, + latestRuleVersion: 5, + }); + expect(ruleTypeRegistry.getLatestRuleVersion()).toBe(5); + }); + }); }); function ruleTypeWithVariables( diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index d73d28950970e..2ed57e878291a 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -14,7 +14,6 @@ import { Logger } from '@kbn/core/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { RunContext, TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import { stateSchemaByVersion } from '@kbn/alerting-state-types'; -import { rawRuleSchema } from './raw_rule_schema'; import { TaskRunnerFactory } from './task_runner'; import { RuleType, @@ -40,6 +39,7 @@ import { AlertingRulesConfig } from '.'; import { AlertsService } from './alerts_service/alerts_service'; import { getRuleTypeIdValidLegacyConsumers } from './rule_type_registry_deprecated_consumers'; import { AlertingConfig } from './config'; +import { rawRuleSchemaV1 } from './saved_objects/schemas/raw_rule'; export interface ConstructorOptions { config: AlertingConfig; @@ -51,6 +51,7 @@ export interface ConstructorOptions { minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; inMemoryMetrics: InMemoryMetrics; alertsService: AlertsService | null; + latestRuleVersion: number; } export interface RegistryRuleType @@ -160,6 +161,7 @@ export class RuleTypeRegistry { private readonly licensing: LicensingPluginSetup; private readonly inMemoryMetrics: InMemoryMetrics; private readonly alertsService: AlertsService | null; + private readonly latestRuleVersion: number; constructor({ config, @@ -171,6 +173,7 @@ export class RuleTypeRegistry { minimumScheduleInterval, inMemoryMetrics, alertsService, + latestRuleVersion, }: ConstructorOptions) { this.config = config; this.logger = logger; @@ -181,6 +184,7 @@ export class RuleTypeRegistry { this.minimumScheduleInterval = minimumScheduleInterval; this.inMemoryMetrics = inMemoryMetrics; this.alertsService = alertsService; + this.latestRuleVersion = latestRuleVersion; } public has(id: string) { @@ -311,7 +315,7 @@ export class RuleTypeRegistry { spaceId: schema.string(), consumer: schema.maybe(schema.string()), }), - indirectParamsSchema: rawRuleSchema, + indirectParamsSchema: rawRuleSchemaV1, }, }); @@ -419,6 +423,7 @@ export class RuleTypeRegistry { name, minimumLicenseRequired ).isValid, + fieldsForAAD, hasFieldsForAAD: Boolean(fieldsForAAD), hasAlertsMappings: !!alerts, validLegacyConsumers, @@ -433,6 +438,10 @@ export class RuleTypeRegistry { public getAllTypes(): string[] { return [...this.ruleTypes.keys()]; } + + public getLatestRuleVersion() { + return this.latestRuleVersion; + } } function normalizedActionVariables(actionVariables: RuleType['actionVariables']) { diff --git a/x-pack/plugins/alerting/server/rules_client_factory.test.ts b/x-pack/plugins/alerting/server/rules_client_factory.test.ts index 9f1a4acb12420..0532a48be01e1 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.test.ts @@ -15,7 +15,7 @@ import { savedObjectsRepositoryMock, } from '@kbn/core/server/mocks'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; -import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { PluginStartContract as ActionsStartContract } from '@kbn/actions-plugin/server'; import { actionsMock, actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; diff --git a/x-pack/plugins/alerting/server/rules_settings_client_factory.test.ts b/x-pack/plugins/alerting/server/rules_settings_client_factory.test.ts index bb278dbf50cdd..6f713f20530e5 100644 --- a/x-pack/plugins/alerting/server/rules_settings_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/rules_settings_client_factory.test.ts @@ -15,7 +15,7 @@ import { savedObjectsServiceMock, loggingSystemMock, } from '@kbn/core/server/mocks'; -import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import { RULES_SETTINGS_SAVED_OBJECT_TYPE } from '../common'; diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index c16356193fd8d..9006bf1cab1b6 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -24,10 +24,12 @@ import { getImportWarnings } from './get_import_warnings'; import { isRuleExportable } from './is_rule_exportable'; import { RuleTypeRegistry } from '../rule_type_registry'; export { partiallyUpdateAlert } from './partially_update_alert'; +export { getLatestRuleVersion, getMinimumCompatibleVersion } from './rule_model_versions'; import { RULES_SETTINGS_SAVED_OBJECT_TYPE, MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, } from '../../common'; +import { ruleModelVersions } from './rule_model_versions'; // Use caution when removing items from this array! Any field which has // ever existed in the rule SO must be included in this array to prevent @@ -106,6 +108,7 @@ export function setupSavedObjects( return isRuleExportable(ruleSavedObject, ruleTypeRegistry, logger); }, }, + modelVersions: ruleModelVersions, }); savedObjects.registerType({ diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index 442dcf0a9469d..bf330be87257a 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -37,6 +37,7 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, + latestRuleVersion: 1, }; }); diff --git a/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.test.ts b/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.test.ts new file mode 100644 index 0000000000000..9afcdaad8e2f4 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CustomSavedObjectsModelVersionMap, + getLatestRuleVersion, + getMinimumCompatibleVersion, +} from './rule_model_versions'; +import { schema } from '@kbn/config-schema'; +import { RawRule } from '../types'; + +describe('rule model versions', () => { + const ruleModelVersions: CustomSavedObjectsModelVersionMap = { + '1': { + changes: [], + schemas: { + create: schema.object({ + name: schema.string(), + }), + }, + isCompatibleWithPreviousVersion: (rawRule) => true, + }, + '2': { + changes: [], + schemas: { + create: schema.object({ + name: schema.string(), + }), + }, + isCompatibleWithPreviousVersion: (rawRule) => false, + }, + '3': { + changes: [], + schemas: { + create: schema.object({ + name: schema.string(), + }), + }, + isCompatibleWithPreviousVersion: (rawRule) => rawRule.name === 'test', + }, + '4': { + changes: [], + schemas: { + create: schema.object({ + name: schema.string(), + }), + }, + isCompatibleWithPreviousVersion: (rawRule) => rawRule.name === 'test', + }, + }; + + const rawRule = { name: 'test' } as RawRule; + const mismatchingRawRule = { enabled: true } as RawRule; + + describe('getMinimumCompatibleVersion', () => { + it('should return the minimum compatible version for the matching rawRule', () => { + expect(getMinimumCompatibleVersion(ruleModelVersions, 1, rawRule)).toBe(1); + expect(getMinimumCompatibleVersion(ruleModelVersions, 2, rawRule)).toBe(2); + expect(getMinimumCompatibleVersion(ruleModelVersions, 3, rawRule)).toBe(2); + expect(getMinimumCompatibleVersion(ruleModelVersions, 4, rawRule)).toBe(2); + }); + it('should return the minimum compatible version for the mismatching rawRule', () => { + expect(getMinimumCompatibleVersion(ruleModelVersions, 3, mismatchingRawRule)).toBe(3); + expect(getMinimumCompatibleVersion(ruleModelVersions, 4, mismatchingRawRule)).toBe(4); + }); + }); + + describe('getLatestRuleVersion', () => { + it('should return the latest rule model version', () => { + expect(getLatestRuleVersion()).toBe(1); + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.ts b/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.ts new file mode 100644 index 0000000000000..38adc17389b23 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/rule_model_versions.ts @@ -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 { + SavedObjectsModelVersion, + SavedObjectsModelVersionMap, +} from '@kbn/core-saved-objects-server'; +import { RawRule } from '../types'; +import { rawRuleSchemaV1 } from './schemas/raw_rule'; + +interface CustomSavedObjectsModelVersion extends SavedObjectsModelVersion { + isCompatibleWithPreviousVersion: (param: RawRule) => boolean; +} + +export interface CustomSavedObjectsModelVersionMap extends SavedObjectsModelVersionMap { + [modelVersion: string]: CustomSavedObjectsModelVersion; +} + +export const ruleModelVersions: CustomSavedObjectsModelVersionMap = { + '1': { + changes: [], + schemas: { + create: rawRuleSchemaV1, + }, + isCompatibleWithPreviousVersion: (rawRule) => true, + }, +}; + +export const getLatestRuleVersion = () => Math.max(...Object.keys(ruleModelVersions).map(Number)); + +export function getMinimumCompatibleVersion( + modelVersions: CustomSavedObjectsModelVersionMap, + version: number, + rawRule: RawRule +): number { + if (version === 1) { + return 1; + } + + if (modelVersions[version].isCompatibleWithPreviousVersion(rawRule)) { + return getMinimumCompatibleVersion(modelVersions, version - 1, rawRule); + } + + return version; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index.ts b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/index.ts similarity index 80% rename from x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index.ts rename to x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/index.ts index e78ffe1c9086a..d5778bcda4a7a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { IndexPatternPrompt } from './index_pattern_prompt'; +export { rawRuleSchema as rawRuleSchemaV1 } from './v1'; diff --git a/x-pack/plugins/alerting/server/raw_rule_schema.ts b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts similarity index 97% rename from x-pack/plugins/alerting/server/raw_rule_schema.ts rename to x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts index 4072b15b19210..76c6241396dc3 100644 --- a/x-pack/plugins/alerting/server/raw_rule_schema.ts +++ b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts @@ -168,6 +168,7 @@ const rawRuleAlertsFilterSchema = schema.object({ key: schema.maybe(schema.string()), params: schema.maybe(schema.recordOf(schema.string(), schema.any())), // better type? value: schema.maybe(schema.string()), + field: schema.maybe(schema.string()), }), $state: schema.maybe( schema.object({ @@ -209,6 +210,7 @@ const rawRuleActionSchema = schema.object({ }) ), alertsFilter: schema.maybe(rawRuleAlertsFilterSchema), + useAlertDataForTemplate: schema.maybe(schema.boolean()), }); export const rawRuleSchema = schema.object({ @@ -266,5 +268,6 @@ export const rawRuleSchema = schema.object({ severity: schema.maybe(schema.string()), }) ), - params: schema.recordOf(schema.string(), schema.any()), + params: schema.recordOf(schema.string(), schema.maybe(schema.any())), + typeVersion: schema.maybe(schema.number()), }); diff --git a/x-pack/plugins/apm/common/data_source.ts b/x-pack/plugins/apm/common/data_source.ts index f3450fe775429..93d8261473692 100644 --- a/x-pack/plugins/apm/common/data_source.ts +++ b/x-pack/plugins/apm/common/data_source.ts @@ -14,7 +14,8 @@ type AnyApmDocumentType = | ApmDocumentType.TransactionEvent | ApmDocumentType.ServiceDestinationMetric | ApmDocumentType.ServiceSummaryMetric - | ApmDocumentType.ErrorEvent; + | ApmDocumentType.ErrorEvent + | ApmDocumentType.SpanEvent; export interface ApmDataSource< TDocumentType extends AnyApmDocumentType = AnyApmDocumentType diff --git a/x-pack/plugins/apm/common/document_type.ts b/x-pack/plugins/apm/common/document_type.ts index 92a17c3125a96..e8a29e8d08c43 100644 --- a/x-pack/plugins/apm/common/document_type.ts +++ b/x-pack/plugins/apm/common/document_type.ts @@ -12,6 +12,7 @@ export enum ApmDocumentType { ServiceDestinationMetric = 'serviceDestinationMetric', ServiceSummaryMetric = 'serviceSummaryMetric', ErrorEvent = 'error', + SpanEvent = 'span', } export type ApmServiceTransactionDocumentType = diff --git a/x-pack/plugins/apm/common/utils/kuery_utils.test.ts b/x-pack/plugins/apm/common/utils/kuery_utils.test.ts new file mode 100644 index 0000000000000..556a0f4968fd6 --- /dev/null +++ b/x-pack/plugins/apm/common/utils/kuery_utils.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright 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 { toKueryFilterFormat, mergeKueries } from './kuery_utils'; + +describe('toKueryFilterFormat', () => { + it('returns a single value', () => { + expect(toKueryFilterFormat('key', ['foo'])).toEqual(`key : "foo"`); + }); + + it('returns multiple values default separator', () => { + expect(toKueryFilterFormat('key', ['foo', 'bar', 'baz'])).toEqual( + `key : "foo" OR key : "bar" OR key : "baz"` + ); + }); + + it('returns multiple values custom separator', () => { + expect(toKueryFilterFormat('key', ['foo', 'bar', 'baz'], 'AND')).toEqual( + `key : "foo" AND key : "bar" AND key : "baz"` + ); + }); + + it('return empty string when no hostname', () => { + expect(toKueryFilterFormat('key', [])).toEqual(''); + }); + + describe('mergeKueries', () => { + it('returns empty string when both kueries are empty', () => { + expect(mergeKueries(['', ''])).toEqual(''); + }); + + it('returns only first kuery when second is empty', () => { + expect(mergeKueries(['host.name: "foo"', ''])).toEqual( + 'host.name: "foo"' + ); + }); + + it('returns second kuery when first is empty', () => { + expect(mergeKueries(['', 'host.name: "foo"'])).toEqual( + 'host.name: "foo"' + ); + }); + + it('returns merged kueries with default separator', () => { + expect( + mergeKueries([ + 'host.name: "foo" OR host.name: "bar"', + 'process.id: "1"', + ]) + ).toEqual('host.name: "foo" OR host.name: "bar" AND process.id: "1"'); + }); + + it('uses custom separator', () => { + expect( + mergeKueries(['host.name: "foo"', 'process.id: "1"'], 'OR') + ).toEqual('host.name: "foo" OR process.id: "1"'); + }); + }); +}); diff --git a/x-pack/plugins/apm/common/utils/kuery_utils.ts b/x-pack/plugins/apm/common/utils/kuery_utils.ts new file mode 100644 index 0000000000000..1caf154a0d5fa --- /dev/null +++ b/x-pack/plugins/apm/common/utils/kuery_utils.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEmpty } from 'lodash'; + +type Separator = 'OR' | 'AND'; + +export const toKueryFilterFormat = ( + key: string, + values: string[], + separator: Separator = 'OR' +) => values.map((value) => `${key} : "${value}"`).join(` ${separator} `); + +export const mergeKueries = (filters: string[], separator: Separator = 'AND') => + filters.filter((filter) => !isEmpty(filter)).join(` ${separator} `); diff --git a/x-pack/plugins/apm/common/utils/to_kuery_filter_format.test.ts b/x-pack/plugins/apm/common/utils/to_kuery_filter_format.test.ts deleted file mode 100644 index 0a1e01d8404f7..0000000000000 --- a/x-pack/plugins/apm/common/utils/to_kuery_filter_format.test.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. - */ -import { toKueryFilterFormat } from './to_kuery_filter_format'; - -describe('toKueryFilterFormat', () => { - it('returns a single value', () => { - expect(toKueryFilterFormat('key', ['foo'])).toEqual(`key : "foo"`); - }); - - it('returns multiple values default separator', () => { - expect(toKueryFilterFormat('key', ['foo', 'bar', 'baz'])).toEqual( - `key : "foo" OR key : "bar" OR key : "baz"` - ); - }); - - it('returns multiple values custom separator', () => { - expect(toKueryFilterFormat('key', ['foo', 'bar', 'baz'], 'AND')).toEqual( - `key : "foo" AND key : "bar" AND key : "baz"` - ); - }); - - it('return empty string when no hostname', () => { - expect(toKueryFilterFormat('key', [])).toEqual(''); - }); -}); diff --git a/x-pack/plugins/apm/dev_docs/telemetry.md b/x-pack/plugins/apm/dev_docs/telemetry.md index e11ea3bcdec33..89293aad75a19 100644 --- a/x-pack/plugins/apm/dev_docs/telemetry.md +++ b/x-pack/plugins/apm/dev_docs/telemetry.md @@ -7,7 +7,7 @@ two types of telemetry, which we'll refer to here as "Data Telemetry" and This document will explain how they are collected and how to make changes to them. -[The telemetry repository has information about accessing the clusters](https://github.com/elastic/telemetry#kibana-access). +[The telemetry repository has information about accessing the clusters](https://github.com/elastic/telemetry#i-just-want-to-see-the-data). Telemetry data is uploaded to the "xpack-phone-home" indices. ## Data Telemetry diff --git a/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx index 28865a8ad5f15..46f5045dc43e6 100644 --- a/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/metrics_details/service_node_metrics/index.tsx @@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import React from 'react'; +import { ApmDocumentType } from '../../../../../common/document_type'; import { getServiceNodeName, SERVICE_NODE_NAME_MISSING, @@ -33,6 +34,7 @@ import { ChartPointerEventContextProvider } from '../../../../context/chart_poin import { useApmParams } from '../../../../hooks/use_apm_params'; import { useApmRouter } from '../../../../hooks/use_apm_router'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; +import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; import { useServiceMetricChartsFetcher } from '../../../../hooks/use_service_metric_charts_fetcher'; import { useTimeRange } from '../../../../hooks/use_time_range'; import { truncate, unit } from '../../../../utils/style'; @@ -83,9 +85,17 @@ export function ServiceNodeMetrics({ serviceNodeName }: Props) { environment, }); + const preferred = usePreferredDataSourceAndBucketSize({ + start, + end, + kuery, + type: ApmDocumentType.ServiceTransactionMetric, + numBuckets: 100, + }); + const { data: { host, containerId } = INITIAL_DATA, status } = useFetcher( (callApmApi) => { - if (start && end) { + if (start && end && preferred) { return callApmApi( 'GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata', { @@ -96,13 +106,15 @@ export function ServiceNodeMetrics({ serviceNodeName }: Props) { start, end, environment, + documentType: preferred.source.documentType, + rollupInterval: preferred.source.rollupInterval, }, }, } ); } }, - [kuery, serviceName, serviceNodeName, start, end, environment] + [kuery, serviceName, serviceNodeName, start, end, environment, preferred] ); const { docLinks } = useApmPluginContext().core; diff --git a/x-pack/plugins/apm/public/components/app/profiling_overview/index.tsx b/x-pack/plugins/apm/public/components/app/profiling_overview/index.tsx index 6c6bd7a356d78..cdd9699ebadc1 100644 --- a/x-pack/plugins/apm/public/components/app/profiling_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/profiling_overview/index.tsx @@ -17,7 +17,9 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { EmbeddableProfilingSearchBar } from '@kbn/observability-shared-plugin/public'; import React, { useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; import { ApmDocumentType } from '../../../../common/document_type'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useLocalStorage } from '../../../hooks/use_local_storage'; @@ -25,17 +27,19 @@ import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferre import { useProfilingPlugin } from '../../../hooks/use_profiling_plugin'; import { useTimeRange } from '../../../hooks/use_time_range'; import { ApmPluginStartDeps } from '../../../plugin'; +import { push } from '../../shared/links/url_helpers'; import { ProfilingFlamegraph } from './profiling_flamegraph'; import { ProfilingTopNFunctions } from './profiling_top_functions'; export function ProfilingOverview() { + const history = useHistory(); const { services } = useKibana(); const { path: { serviceName }, query: { rangeFrom, rangeTo, environment, kuery }, } = useApmParams('/services/{serviceName}/profiling'); const { isProfilingAvailable } = useProfilingPlugin(); - const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const { start, end, refreshTimeRange } = useTimeRange({ rangeFrom, rangeTo }); const preferred = usePreferredDataSourceAndBucketSize({ start, end, @@ -43,6 +47,7 @@ export function ProfilingOverview() { type: ApmDocumentType.TransactionMetric, numBuckets: 20, }); + const [ apmUniversalProfilingShowCallout, setAPMUniversalProfilingShowCallout, @@ -67,6 +72,7 @@ export function ProfilingOverview() { end={end} environment={environment} dataSource={preferred?.source} + kuery={kuery} /> ), @@ -87,12 +93,13 @@ export function ProfilingOverview() { startIndex={0} endIndex={10} dataSource={preferred?.source} + kuery={kuery} /> ), }, ]; - }, [end, environment, preferred?.source, serviceName, start]); + }, [end, environment, kuery, preferred?.source, serviceName, start]); if (!isProfilingAvailable) { return null; @@ -147,6 +154,22 @@ export function ProfilingOverview() { )} + { + push(history, { + query: { + kuery: next.query, + rangeFrom: next.dateRange.from, + rangeTo: next.dateRange.to, + }, + }); + }} + onRefresh={refreshTimeRange} + /> + ; + kuery: string; } export function ProfilingFlamegraph({ @@ -44,6 +48,7 @@ export function ProfilingFlamegraph({ serviceName, environment, dataSource, + kuery, }: Props) { const { profilingLocators } = useProfilingPlugin(); @@ -61,13 +66,14 @@ export function ProfilingFlamegraph({ environment, documentType: dataSource.documentType, rollupInterval: dataSource.rollupInterval, + kuery, }, }, } ); } }, - [dataSource, serviceName, start, end, environment] + [dataSource, serviceName, start, end, environment, kuery] ); const hostNamesKueryFormat = toKueryFilterFormat( @@ -86,7 +92,7 @@ export function ProfilingFlamegraph({ {i18n.translate('xpack.apm.profiling.flamegraph.link', { diff --git a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx index 0c2a225040728..7b428802fbfc1 100644 --- a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx +++ b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx @@ -9,13 +9,16 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { EmbeddableFunctions } from '@kbn/observability-shared-plugin/public'; import React from 'react'; +import { ApmDataSourceWithSummary } from '../../../../common/data_source'; +import { ApmDocumentType } from '../../../../common/document_type'; import { HOST_NAME } from '../../../../common/es_fields/apm'; -import { toKueryFilterFormat } from '../../../../common/utils/to_kuery_filter_format'; +import { + mergeKueries, + toKueryFilterFormat, +} from '../../../../common/utils/kuery_utils'; import { isPending, useFetcher } from '../../../hooks/use_fetcher'; import { useProfilingPlugin } from '../../../hooks/use_profiling_plugin'; import { HostnamesFilterWarning } from './host_names_filter_warning'; -import { ApmDataSourceWithSummary } from '../../../../common/data_source'; -import { ApmDocumentType } from '../../../../common/document_type'; interface Props { serviceName: string; @@ -27,6 +30,7 @@ interface Props { dataSource?: ApmDataSourceWithSummary< ApmDocumentType.TransactionMetric | ApmDocumentType.TransactionEvent >; + kuery: string; } export function ProfilingTopNFunctions({ @@ -37,6 +41,7 @@ export function ProfilingTopNFunctions({ startIndex, endIndex, dataSource, + kuery, }: Props) { const { profilingLocators } = useProfilingPlugin(); @@ -56,13 +61,23 @@ export function ProfilingTopNFunctions({ endIndex, documentType: dataSource.documentType, rollupInterval: dataSource.rollupInterval, + kuery, }, }, } ); } }, - [dataSource, serviceName, start, end, environment, startIndex, endIndex] + [ + dataSource, + serviceName, + start, + end, + environment, + startIndex, + endIndex, + kuery, + ] ); const hostNamesKueryFormat = toKueryFilterFormat( @@ -81,7 +96,7 @@ export function ProfilingTopNFunctions({ {i18n.translate('xpack.apm.profiling.topnFunctions.link', { 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 081a24ceb4427..b3e36fc8bebb9 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 @@ -327,6 +327,7 @@ export function ServiceList({ } = useApmParams('/services'); const { kuery } = query; + const { fallbackToTransactions } = useFallbackToTransactionsFetcher({ kuery, }); diff --git a/x-pack/plugins/apm/public/components/app/top_traces_overview/index.tsx b/x-pack/plugins/apm/public/components/app/top_traces_overview/index.tsx index 486a54b400098..3c314f6389770 100644 --- a/x-pack/plugins/apm/public/components/app/top_traces_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/top_traces_overview/index.tsx @@ -19,12 +19,12 @@ export function TopTracesOverview() { const { query: { environment, kuery, rangeFrom, rangeTo }, } = useApmParams('/traces'); + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const { fallbackToTransactions } = useFallbackToTransactionsFetcher({ kuery, }); - const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const response = useProgressiveFetcher( (callApmApi) => { if (start && end) { 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 56deaaa2e6d6e..f2601e392b276 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 @@ -371,9 +371,7 @@ export const serviceDetailRoute = { }), element: , searchBarOptions: { - showTimeComparison: false, - showTransactionTypeSelector: false, - showQueryInput: false, + hidden: true, }, }), }, diff --git a/x-pack/plugins/apm/public/hooks/use_current_user.ts b/x-pack/plugins/apm/public/hooks/use_current_user.ts index 0e95bb27bbb87..c700ae7bd288d 100644 --- a/x-pack/plugins/apm/public/hooks/use_current_user.ts +++ b/x-pack/plugins/apm/public/hooks/use_current_user.ts @@ -7,7 +7,7 @@ import { useState, useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { ApmPluginStartDeps } from '../plugin'; export function useCurrentUser() { diff --git a/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx index f1d30f4237071..6ffebcb95f2aa 100644 --- a/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { getKueryWithMobileFilters } from '../../common/utils/get_kuery_with_mobile_filters'; import { useApmParams } from './use_apm_params'; import { useFetcher } from './use_fetcher'; @@ -33,11 +34,17 @@ export function useFallbackToTransactionsFetcher({ kuery }: { kuery: string }) { const { data = { fallbackToTransactions: false } } = useFetcher( (callApmApi) => { - return callApmApi('GET /internal/apm/fallback_to_transactions', { - params: { - query: { kuery: kueryWithFilters, start, end }, - }, - }); + if (start && end) { + return callApmApi('GET /internal/apm/fallback_to_transactions', { + params: { + query: { + kuery: kueryWithFilters, + start, + end, + }, + }, + }); + } }, [kueryWithFilters, start, end] ); diff --git a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts index 346921960f92d..4524d5121b78d 100644 --- a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts +++ b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts @@ -9,7 +9,6 @@ import { sum } from 'lodash'; import objectHash from 'object-hash'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { rangeQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import { getOffsetInMs } from '../../../../common/utils/get_offset_in_ms'; import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values'; @@ -28,6 +27,8 @@ import { import { getBucketSize } from '../../../../common/utils/get_bucket_size'; import { EventOutcome } from '../../../../common/event_outcome'; import { NodeType } from '../../../../common/connections'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; import { excludeRumExitSpansQuery } from '../exclude_rum_exit_spans_query'; import { APMEventClient } from '../../helpers/create_es_client/create_apm_event_client'; import { getDocumentTypeFilterForServiceDestinationStatistics } from '../../helpers/spans/get_is_using_service_destination_metrics'; @@ -55,7 +56,12 @@ export const getStats = async ({ const response = await apmEventClient.search('get_connection_stats', { apm: { - events: [ProcessorEvent.metric], + sources: [ + { + documentType: ApmDocumentType.ServiceDestinationMetric, + rollupInterval: RollupInterval.OneMinute, + }, + ], }, body: { track_total_hits: true, diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/get_request_base.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/get_request_base.ts index 1046a2ad47cfe..85ac6689f5df8 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/get_request_base.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/get_request_base.ts @@ -57,9 +57,9 @@ export function getRequestBase(options: { if ('sources' in options.apm) { options.apm.sources.forEach((source) => { - const { getQuery } = getConfigForDocumentType(source.documentType); - if (getQuery) { - filters.push(getQuery(source.rollupInterval)); + const documentTypeConfig = getConfigForDocumentType(source.documentType); + if ('getQuery' in documentTypeConfig) { + filters.push(documentTypeConfig.getQuery(source.rollupInterval)); } }); } diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts index fcac29b1d57d5..53e4c91f384f3 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts @@ -62,13 +62,11 @@ type APMEventTermsEnumRequest = APMEventWrapper; type APMEventEqlSearchRequest = APMEventWrapper; type APMEventFieldCapsRequest = APMEventWrapper; -// These keys shoul all be `ProcessorEvent.x`, but until TypeScript 4.2 we're inlining them here. -// See https://github.com/microsoft/TypeScript/issues/37888 type TypeOfProcessorEvent = { - error: APMError; - transaction: Transaction; - span: Span; - metric: Metric; + [ProcessorEvent.error]: APMError; + [ProcessorEvent.transaction]: Transaction; + [ProcessorEvent.span]: Span; + [ProcessorEvent.metric]: Metric; }[T]; type TypedLogEventSearchResponse = @@ -77,15 +75,13 @@ type TypedLogEventSearchResponse = type TypedSearchResponse = InferSearchResponseOf< TypeOfProcessorEvent< - ValuesType< - TParams['apm'] extends { events: ProcessorEvent[] } - ? TParams['apm']['events'] - : TParams['apm'] extends { sources: ApmDataSource[] } - ? ProcessorEventOfDocumentType< - ValuesType['documentType'] - > - : never - > + TParams['apm'] extends { events: ProcessorEvent[] } + ? ValuesType + : TParams['apm'] extends { sources: ApmDataSource[] } + ? ProcessorEventOfDocumentType< + ValuesType['documentType'] + > + : never >, TParams >; diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts index a3f5ff8d5683c..bcafd76b8e222 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/document_type.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { ApmDocumentType } from '../../../../common/document_type'; import { @@ -33,18 +32,11 @@ function getDefaultFilter( ]; } -const documentTypeConfigMap: Record< - ApmDocumentType, - { - processorEvent: ProcessorEvent; - getQuery?: (rollupInterval: RollupInterval) => QueryDslQueryContainer; - rollupIntervals: RollupInterval[]; - } -> = { +const documentTypeConfigMap = { [ApmDocumentType.ServiceTransactionMetric]: { processorEvent: ProcessorEvent.metric, - getQuery: (rollupInterval) => ({ + getQuery: (rollupInterval: RollupInterval) => ({ bool: { filter: getDefaultFilter('service_transaction', rollupInterval), }, @@ -53,7 +45,7 @@ const documentTypeConfigMap: Record< }, [ApmDocumentType.ServiceSummaryMetric]: { processorEvent: ProcessorEvent.metric, - getQuery: (rollupInterval) => ({ + getQuery: (rollupInterval: RollupInterval) => ({ bool: { filter: getDefaultFilter('service_summary', rollupInterval), }, @@ -62,7 +54,7 @@ const documentTypeConfigMap: Record< }, [ApmDocumentType.TransactionMetric]: { processorEvent: ProcessorEvent.metric, - getQuery: (rollupInterval) => ({ + getQuery: (rollupInterval: RollupInterval) => ({ bool: { filter: rollupInterval === RollupInterval.OneMinute @@ -79,7 +71,7 @@ const documentTypeConfigMap: Record< [ApmDocumentType.ServiceDestinationMetric]: { processorEvent: ProcessorEvent.metric, rollupIntervals: defaultRollupIntervals, - getQuery: (rollupInterval) => ({ + getQuery: (rollupInterval: RollupInterval) => ({ bool: { filter: rollupInterval === RollupInterval.OneMinute @@ -92,7 +84,11 @@ const documentTypeConfigMap: Record< processorEvent: ProcessorEvent.error, rollupIntervals: [RollupInterval.None], }, -}; + [ApmDocumentType.SpanEvent]: { + processorEvent: ProcessorEvent.span, + rollupIntervals: [RollupInterval.None], + }, +} as const; type DocumentTypeConfigOf = typeof documentTypeConfigMap[TApmDocumentType]; diff --git a/x-pack/plugins/apm/server/lib/helpers/transactions/__snapshots__/get_is_using_transaction_events.test.ts.snap b/x-pack/plugins/apm/server/lib/helpers/transactions/__snapshots__/get_is_using_transaction_events.test.ts.snap index 325ce29af118d..192df5cc8eb3a 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transactions/__snapshots__/get_is_using_transaction_events.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/helpers/transactions/__snapshots__/get_is_using_transaction_events.test.ts.snap @@ -96,8 +96,11 @@ Array [ "get_has_transactions", Object { "apm": Object { - "events": Array [ - "transaction", + "sources": Array [ + Object { + "documentType": "transactionEvent", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/lib/helpers/transactions/get_is_using_transaction_events.ts b/x-pack/plugins/apm/server/lib/helpers/transactions/get_is_using_transaction_events.ts index 20832c70d007b..0ba5fb5f92afd 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transactions/get_is_using_transaction_events.ts +++ b/x-pack/plugins/apm/server/lib/helpers/transactions/get_is_using_transaction_events.ts @@ -6,10 +6,11 @@ */ import { kqlQuery, rangeQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { getSearchTransactionsEvents } from '.'; import { APMEventClient } from '../create_es_client/create_apm_event_client'; import { SearchAggregatedTransactionSetting } from '../../../../common/aggregated_transactions'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; import { APMConfig } from '../../..'; export async function getIsUsingTransactionEvents({ @@ -63,7 +64,12 @@ async function getHasTransactions({ }) { const response = await apmEventClient.search('get_has_transactions', { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: 1, diff --git a/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/get_buckets.test.ts.snap b/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/get_buckets.test.ts.snap index 54705647dc6a4..4145b9ae31da1 100644 --- a/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/get_buckets.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/get_buckets.test.ts.snap @@ -6,8 +6,11 @@ Array [ "get_error_distribution_buckets", Object { "apm": Object { - "events": Array [ - "error", + "sources": Array [ + Object { + "documentType": "error", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/queries.test.ts.snap index a3127ee28c0b8..480283b7a690c 100644 --- a/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/errors/distribution/__snapshots__/queries.test.ts.snap @@ -3,8 +3,11 @@ exports[`error distribution queries fetches an error distribution 1`] = ` Object { "apm": Object { - "events": Array [ - "error", + "sources": Array [ + Object { + "documentType": "error", + "rollupInterval": "none", + }, ], }, "body": Object { @@ -50,8 +53,11 @@ Object { exports[`error distribution queries fetches an error distribution with a group id 1`] = ` Object { "apm": Object { - "events": Array [ - "error", + "sources": Array [ + Object { + "documentType": "error", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.test.ts b/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.test.ts index 4a3fc6d969cd0..a8994fd4ec2c6 100644 --- a/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.test.ts +++ b/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.test.ts @@ -6,7 +6,8 @@ */ import { getBuckets } from './get_buckets'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; describe('get buckets', () => { let clientSpy: jest.Mock; @@ -42,6 +43,11 @@ describe('get buckets', () => { it('should limit query results to error documents', () => { const query = clientSpy.mock.calls[0][1]; - expect(query.apm.events).toEqual([ProcessorEvent.error]); + expect(query.apm.sources).toEqual([ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ]); }); }); diff --git a/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.ts index 72e9b1c1b2d55..83e9d4475bfb8 100644 --- a/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.ts +++ b/x-pack/plugins/apm/server/routes/errors/distribution/get_buckets.ts @@ -10,8 +10,9 @@ import { kqlQuery, termQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { ApmDocumentType } from '../../../../common/document_type'; import { ERROR_GROUP_ID, SERVICE_NAME } from '../../../../common/es_fields/apm'; +import { RollupInterval } from '../../../../common/rollup'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -36,7 +37,12 @@ export async function getBuckets({ }) { const params = { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/errors/erroneous_transactions/get_top_erroneous_transactions.ts b/x-pack/plugins/apm/server/routes/errors/erroneous_transactions/get_top_erroneous_transactions.ts index 31695c0a127d2..41f49959aa70c 100644 --- a/x-pack/plugins/apm/server/routes/errors/erroneous_transactions/get_top_erroneous_transactions.ts +++ b/x-pack/plugins/apm/server/routes/errors/erroneous_transactions/get_top_erroneous_transactions.ts @@ -17,7 +17,6 @@ import { kqlQuery, termQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { keyBy } from 'lodash'; import { ERROR_GROUP_ID, @@ -28,6 +27,8 @@ import { import { environmentQuery } from '../../../../common/utils/environment_query'; import { getBucketSize } from '../../../../common/utils/get_bucket_size'; import { getOffsetInMs } from '../../../../common/utils/get_offset_in_ms'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; async function getTopErroneousTransactions({ @@ -65,7 +66,12 @@ async function getTopErroneousTransactions({ const res = await apmEventClient.search('get_top_erroneous_transactions', { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, @@ -112,17 +118,19 @@ async function getTopErroneousTransactions({ return ( res.aggregations?.top_five_transactions.buckets.map( - ({ key, doc_count: docCount, sample, timeseries }) => ({ - transactionName: key as string, - transactionType: sample.hits.hits[0]._source.transaction?.type, - occurrences: docCount, - timeseries: timeseries.buckets.map((timeseriesBucket) => { - return { - x: timeseriesBucket.key + offsetInMs, - y: timeseriesBucket.doc_count, - }; - }), - }) + ({ key, doc_count: docCount, sample, timeseries }) => { + return { + transactionName: key as string, + transactionType: sample.hits.hits[0]._source.transaction?.type, + occurrences: docCount, + timeseries: timeseries.buckets.map((timeseriesBucket) => { + return { + x: timeseriesBucket.key + offsetInMs, + y: timeseriesBucket.doc_count, + }; + }), + }; + } ) ?? [] ); } diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts index cf799a47f7dc4..182fe0a1cdd8a 100644 --- a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts @@ -11,7 +11,6 @@ import { rangeQuery, termQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { ERROR_CULPRIT, ERROR_EXC_HANDLED, @@ -26,6 +25,8 @@ import { import { environmentQuery } from '../../../../common/utils/environment_query'; import { getErrorName } from '../../../lib/helpers/get_error_name'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; export type ErrorGroupMainStatisticsResponse = Array<{ groupId: string; @@ -75,7 +76,12 @@ export async function getErrorGroupMainStatistics({ 'get_error_group_main_statistics', { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, @@ -128,16 +134,19 @@ export async function getErrorGroupMainStatistics({ ); return ( - response.aggregations?.error_groups.buckets.map((bucket) => ({ - groupId: bucket.key as string, - name: getErrorName(bucket.sample.hits.hits[0]._source), - lastSeen: new Date( - bucket.sample.hits.hits[0]?._source['@timestamp'] - ).getTime(), - occurrences: bucket.doc_count, - culprit: bucket.sample.hits.hits[0]?._source.error.culprit, - handled: bucket.sample.hits.hits[0]?._source.error.exception?.[0].handled, - type: bucket.sample.hits.hits[0]?._source.error.exception?.[0].type, - })) ?? [] + response.aggregations?.error_groups.buckets.map((bucket) => { + return { + groupId: bucket.key as string, + name: getErrorName(bucket.sample.hits.hits[0]._source), + lastSeen: new Date( + bucket.sample.hits.hits[0]._source['@timestamp'] + ).getTime(), + occurrences: bucket.doc_count, + culprit: bucket.sample.hits.hits[0]._source.error.culprit, + handled: + bucket.sample.hits.hits[0]._source.error.exception?.[0].handled, + type: bucket.sample.hits.hits[0]._source.error.exception?.[0].type, + }; + }) ?? [] ); } diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts index 2796ec590ad42..0a154d3ad13fa 100644 --- a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts @@ -6,7 +6,6 @@ */ import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { ERROR_GROUP_ID, @@ -16,6 +15,8 @@ import { } from '../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; const ERROR_SAMPLES_SIZE = 10000; @@ -41,9 +42,14 @@ export async function getErrorGroupSampleIds({ start: number; end: number; }): Promise { - const params = { + const resp = await apmEventClient.search('get_error_group_sample_ids', { apm: { - events: [ProcessorEvent.error as const], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: ERROR_SAMPLES_SIZE, @@ -66,13 +72,11 @@ export async function getErrorGroupSampleIds({ { '@timestamp': { order: 'desc' } }, // sort by timestamp to get the most recent error ] as const), }, - }; - - const resp = await apmEventClient.search( - 'get_error_group_sample_ids', - params - ); - const errorSampleIds = resp.hits.hits.map((item) => item._source.error.id); + }); + const errorSampleIds = resp.hits.hits.map((item) => { + const source = item._source; + return source.error.id; + }); return { errorSampleIds, diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts index cb11c0154be61..348949d3ecca5 100644 --- a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts @@ -6,9 +6,10 @@ */ import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { ERROR_ID, SERVICE_NAME } from '../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../common/utils/environment_query'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { getTransaction } from '../../transactions/get_transaction'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; @@ -38,7 +39,12 @@ export async function getErrorSampleDetails({ }): Promise { const params = { apm: { - events: [ProcessorEvent.error as const], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent as const, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/metrics/serverless/get_serverless_summary.ts b/x-pack/plugins/apm/server/routes/metrics/serverless/get_serverless_summary.ts index 8deb85264e019..b6ea22e03d506 100644 --- a/x-pack/plugins/apm/server/routes/metrics/serverless/get_serverless_summary.ts +++ b/x-pack/plugins/apm/server/routes/metrics/serverless/get_serverless_summary.ts @@ -10,6 +10,7 @@ import { kqlQuery, rangeQuery, } from '@kbn/observability-plugin/server'; +import { ApmDocumentType } from '../../../../common/document_type'; import { FAAS_BILLED_DURATION, FAAS_DURATION, @@ -20,6 +21,7 @@ import { METRIC_SYSTEM_TOTAL_MEMORY, SERVICE_NAME, } from '../../../../common/es_fields/apm'; +import { RollupInterval } from '../../../../common/rollup'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { computeUsageAvgScript } from './get_compute_usage_chart'; @@ -52,7 +54,12 @@ async function getServerlessTransactionThroughput({ }) { const params = { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: true, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_device_os_app.ts b/x-pack/plugins/apm/server/routes/mobile/get_device_os_app.ts index e2194b994b02d..80fae958b6c53 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_device_os_app.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_device_os_app.ts @@ -10,7 +10,6 @@ import { kqlQuery, rangeQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { DEVICE_MODEL_IDENTIFIER, HOST_OS_VERSION, @@ -19,6 +18,8 @@ import { TRANSACTION_TYPE, } from '../../../common/es_fields/apm'; import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; export async function getDeviceOSApp({ @@ -42,7 +43,12 @@ export async function getDeviceOSApp({ }) { return await apmEventClient.search('get_mobile_device_os_app', { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_crash_rate.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_crash_rate.ts index bf498bf704607..e60d0ff15c624 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_mobile_crash_rate.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_crash_rate.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { kqlQuery, rangeQuery, @@ -23,6 +22,8 @@ import { import { environmentQuery } from '../../../common/utils/environment_query'; import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; export interface CrashRateTimeseries { currentPeriod: { timeseries: Coordinate[]; value: Maybe }; @@ -70,7 +71,12 @@ async function getMobileCrashTimeseries({ const response = await apmEventClient.search('get_mobile_crash_rate', { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_crashes_by_location.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_crashes_by_location.ts index 855ae8fb35d05..e91214af444b1 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_mobile_crashes_by_location.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_crashes_by_location.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { kqlQuery, rangeQuery, @@ -16,6 +15,8 @@ import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_ev import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; import { getBucketSize } from '../../../common/utils/get_bucket_size'; import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; interface Props { kuery: string; @@ -64,7 +65,12 @@ export async function getCrashesByLocation({ }; const response = await apmEventClient.search('get_mobile_location_crashes', { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions.ts index e3ccd83c20bf0..9168101ebc40e 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { kqlQuery, rangeQuery, @@ -23,6 +22,8 @@ import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_ev import { getBucketSize } from '../../../common/utils/get_bucket_size'; import { Coordinate } from '../../../typings/timeseries'; import { Maybe } from '../../../typings/common'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; export interface SessionsTimeseries { currentPeriod: { timeseries: Coordinate[]; value: Maybe }; @@ -70,7 +71,12 @@ async function getSessionTimeseries({ const response = await apmEventClient.search('get_mobile_sessions', { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions_by_location.ts b/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions_by_location.ts index 95ad146f585f0..7543d097d888b 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions_by_location.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_mobile_sessions_by_location.ts @@ -10,12 +10,13 @@ import { kqlQuery, rangeQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { SERVICE_NAME, SESSION_ID } from '../../../common/es_fields/apm'; import { environmentQuery } from '../../../common/utils/environment_query'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; import { getBucketSize } from '../../../common/utils/get_bucket_size'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; interface Props { kuery: string; @@ -65,7 +66,12 @@ export async function getSessionsByLocation({ const response = await apmEventClient.search('get_mobile_location_sessions', { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/mobile/get_nct.ts b/x-pack/plugins/apm/server/routes/mobile/get_nct.ts index 829f40962f84d..f84701ecfb293 100644 --- a/x-pack/plugins/apm/server/routes/mobile/get_nct.ts +++ b/x-pack/plugins/apm/server/routes/mobile/get_nct.ts @@ -10,12 +10,13 @@ import { kqlQuery, rangeQuery, } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { NETWORK_CONNECTION_TYPE, SERVICE_NAME, } from '../../../common/es_fields/apm'; import { environmentQuery } from '../../../common/utils/environment_query'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; export async function getNCT({ @@ -38,7 +39,12 @@ export async function getNCT({ }) { return await apmEventClient.search('get_mobile_nct', { apm: { - events: [ProcessorEvent.span], + sources: [ + { + documentType: ApmDocumentType.SpanEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/profiling/route.ts b/x-pack/plugins/apm/server/routes/profiling/route.ts index 9d5853c288336..16b1b5fd56614 100644 --- a/x-pack/plugins/apm/server/routes/profiling/route.ts +++ b/x-pack/plugins/apm/server/routes/profiling/route.ts @@ -10,11 +10,15 @@ import type { BaseFlameGraph, TopNFunctions } from '@kbn/profiling-utils'; import * as t from 'io-ts'; import { profilingUseLegacyFlamegraphAPI } from '@kbn/observability-plugin/common'; import { HOST_NAME } from '../../../common/es_fields/apm'; -import { toKueryFilterFormat } from '../../../common/utils/to_kuery_filter_format'; +import { + mergeKueries, + toKueryFilterFormat, +} from '../../../common/utils/kuery_utils'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { environmentRt, + kueryRt, rangeRt, serviceTransactionDataSourceRt, } from '../default_api_types'; @@ -28,6 +32,7 @@ const profilingFlamegraphRoute = createApmServerRoute({ rangeRt, environmentRt, serviceTransactionDataSourceRt, + kueryRt, ]), }), options: { tags: ['access:apm'] }, @@ -48,7 +53,7 @@ const profilingFlamegraphRoute = createApmServerRoute({ await plugins.profilingDataAccess?.start(), ]); if (profilingDataAccessStart) { - const { start, end, environment, documentType, rollupInterval } = + const { start, end, environment, documentType, rollupInterval, kuery } = params.query; const { serviceName } = params.path; @@ -71,7 +76,10 @@ const profilingFlamegraphRoute = createApmServerRoute({ esClient: esClient.asCurrentUser, rangeFromMs: start, rangeToMs: end, - kuery: toKueryFilterFormat(HOST_NAME, serviceHostNames), + kuery: mergeKueries([ + `(${toKueryFilterFormat(HOST_NAME, serviceHostNames)})`, + kuery, + ]), useLegacyFlamegraphAPI, }); @@ -91,6 +99,7 @@ const profilingFunctionsRoute = createApmServerRoute({ environmentRt, serviceTransactionDataSourceRt, t.type({ startIndex: toNumberRt, endIndex: toNumberRt }), + kueryRt, ]), }), options: { tags: ['access:apm'] }, @@ -113,6 +122,7 @@ const profilingFunctionsRoute = createApmServerRoute({ endIndex, documentType, rollupInterval, + kuery, } = params.query; const { serviceName } = params.path; @@ -134,7 +144,10 @@ const profilingFunctionsRoute = createApmServerRoute({ esClient: esClient.asCurrentUser, rangeFromMs: start, rangeToMs: end, - kuery: toKueryFilterFormat(HOST_NAME, serviceHostNames), + kuery: mergeKueries([ + `(${toKueryFilterFormat(HOST_NAME, serviceHostNames)})`, + kuery, + ]), startIndex, endIndex, }); diff --git a/x-pack/plugins/apm/server/routes/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/routes/services/get_service_node_metadata.ts index e327bf1e7cdb7..033e343084783 100644 --- a/x-pack/plugins/apm/server/routes/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/routes/services/get_service_node_metadata.ts @@ -6,7 +6,6 @@ */ import { kqlQuery, rangeQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { HOST_NAME, CONTAINER_ID } from '../../../common/es_fields/apm'; import { NOT_AVAILABLE_LABEL } from '../../../common/i18n'; import { SERVICE_NAME, SERVICE_NODE_NAME } from '../../../common/es_fields/apm'; @@ -15,6 +14,8 @@ import { serviceNodeNameQuery, } from '../../../common/utils/environment_query'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; +import { ApmServiceTransactionDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; export interface ServiceNodeMetadataResponse { host: string | number; @@ -29,6 +30,8 @@ export async function getServiceNodeMetadata({ start, end, environment, + documentType, + rollupInterval, }: { kuery: string; serviceName: string; @@ -37,10 +40,17 @@ export async function getServiceNodeMetadata({ start: number; end: number; environment: string; + documentType: ApmServiceTransactionDocumentType; + rollupInterval: RollupInterval; }): Promise { const params = { apm: { - events: [ProcessorEvent.metric], + sources: [ + { + documentType, + rollupInterval, + }, + ], }, body: { track_total_hits: false, @@ -78,14 +88,14 @@ export async function getServiceNodeMetadata({ }, }; - const response = await apmEventClient.search( + const { aggregations } = await apmEventClient.search( 'get_service_node_metadata', params ); return { - host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL, + host: aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL, containerId: - response.aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL, + aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL, }; } diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 24dc79ea668b4..0a51a3e88379f 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -368,14 +368,20 @@ const serviceNodeMetadataRoute = createApmServerRoute({ serviceName: t.string, serviceNodeName: t.string, }), - query: t.intersection([kueryRt, rangeRt, environmentRt]), + query: t.intersection([ + kueryRt, + rangeRt, + environmentRt, + serviceTransactionDataSourceRt, + ]), }), options: { tags: ['access:apm'] }, handler: async (resources): Promise => { const apmEventClient = await getApmEventClient(resources); const { params } = resources; const { serviceName, serviceNodeName } = params.path; - const { kuery, start, end, environment } = params.query; + const { kuery, start, end, environment, documentType, rollupInterval } = + params.query; return getServiceNodeMetadata({ kuery, @@ -385,6 +391,8 @@ const serviceNodeMetadataRoute = createApmServerRoute({ start, end, environment, + documentType, + rollupInterval, }); }, }); diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link/__snapshots__/get_transaction.test.ts.snap b/x-pack/plugins/apm/server/routes/settings/custom_link/__snapshots__/get_transaction.test.ts.snap index 3a738243cf5c7..ea8d4318f4c50 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link/__snapshots__/get_transaction.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/settings/custom_link/__snapshots__/get_transaction.test.ts.snap @@ -3,8 +3,11 @@ exports[`custom link get transaction fetches with all filter 1`] = ` Object { "apm": Object { - "events": Array [ - "transaction", + "sources": Array [ + Object { + "documentType": "transactionEvent", + "rollupInterval": "none", + }, ], }, "body": Object { @@ -52,8 +55,11 @@ Object { exports[`custom link get transaction fetches without filter 1`] = ` Object { "apm": Object { - "events": Array [ - "transaction", + "sources": Array [ + Object { + "documentType": "transactionEvent", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link/get_transaction.ts b/x-pack/plugins/apm/server/routes/settings/custom_link/get_transaction.ts index d454b447b17f9..58cdd55b2d443 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link/get_transaction.ts +++ b/x-pack/plugins/apm/server/routes/settings/custom_link/get_transaction.ts @@ -7,7 +7,8 @@ import * as t from 'io-ts'; import { compact } from 'lodash'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; import { filterOptionsRt } from './custom_link_types'; import { splitFilterValueByComma } from './helper'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; @@ -29,10 +30,15 @@ export async function getTransaction({ }) ); - const params = { + const resp = await apmEventClient.search('get_transaction_for_custom_link', { terminate_after: 1, apm: { - events: [ProcessorEvent.transaction as const], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, @@ -43,10 +49,6 @@ export async function getTransaction({ }, }, }, - }; - const resp = await apmEventClient.search( - 'get_transaction_for_custom_link', - params - ); + }); return resp.hits.hits[0]?._source; } diff --git a/x-pack/plugins/apm/server/routes/traces/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/traces/__snapshots__/queries.test.ts.snap index a490aec44a366..d64c33a421e19 100644 --- a/x-pack/plugins/apm/server/routes/traces/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/traces/__snapshots__/queries.test.ts.snap @@ -3,8 +3,11 @@ exports[`trace queries fetches a trace 1`] = ` Object { "apm": Object { - "events": Array [ - "error", + "sources": Array [ + Object { + "documentType": "error", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts index a1637b29d8e78..3a3e9b8fe2952 100644 --- a/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/routes/traces/get_trace_items.ts @@ -55,6 +55,8 @@ import { } from '../../../common/waterfall/typings'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { getSpanLinksCountById } from '../span_links/get_linked_children'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; export interface TraceItems { exceedsMax: boolean; @@ -87,7 +89,12 @@ export async function getTraceItems({ const errorResponsePromise = apmEventClient.search('get_errors_docs', { apm: { - events: [ProcessorEvent.error], + sources: [ + { + documentType: ApmDocumentType.ErrorEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/apm/server/routes/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/transactions/__snapshots__/queries.test.ts.snap index 4ff86970a611a..deb1dec096f08 100644 --- a/x-pack/plugins/apm/server/routes/transactions/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/routes/transactions/__snapshots__/queries.test.ts.snap @@ -3,8 +3,11 @@ exports[`transaction queries fetches a transaction 1`] = ` Object { "apm": Object { - "events": Array [ - "transaction", + "sources": Array [ + Object { + "documentType": "transactionEvent", + "rollupInterval": "none", + }, ], }, "body": Object { diff --git a/x-pack/plugins/apm/server/routes/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/routes/transactions/get_transaction/index.ts index 77935244361b6..8854f3075e59b 100644 --- a/x-pack/plugins/apm/server/routes/transactions/get_transaction/index.ts +++ b/x-pack/plugins/apm/server/routes/transactions/get_transaction/index.ts @@ -6,10 +6,11 @@ */ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; -import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { TRACE_ID, TRANSACTION_ID } from '../../../../common/es_fields/apm'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { ApmDocumentType } from '../../../../common/document_type'; +import { RollupInterval } from '../../../../common/rollup'; export async function getTransaction({ transactionId, @@ -26,7 +27,12 @@ export async function getTransaction({ }) { const resp = await apmEventClient.search('get_transaction', { apm: { - events: [ProcessorEvent.transaction], + sources: [ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }, + ], }, body: { track_total_hits: false, diff --git a/x-pack/plugins/asset_manager/common/constants_routes.ts b/x-pack/plugins/asset_manager/common/constants_routes.ts index 969df93d1bc9d..6bbde84cc668b 100644 --- a/x-pack/plugins/asset_manager/common/constants_routes.ts +++ b/x-pack/plugins/asset_manager/common/constants_routes.ts @@ -18,3 +18,4 @@ export const GET_ASSETS_DIFF = base('/assets/diff'); export const GET_HOSTS = base('/assets/hosts'); export const GET_SERVICES = base('/assets/services'); export const GET_CONTAINERS = base('/assets/containers'); +export const GET_PODS = base('/assets/pods'); diff --git a/x-pack/plugins/asset_manager/common/types_api.ts b/x-pack/plugins/asset_manager/common/types_api.ts index dd555c69328e5..8c6d7cf5fb0a1 100644 --- a/x-pack/plugins/asset_manager/common/types_api.ts +++ b/x-pack/plugins/asset_manager/common/types_api.ts @@ -172,6 +172,7 @@ export const assetFiltersSingleKindRT = rt.exact( id: rt.string, ['cloud.provider']: rt.string, ['cloud.region']: rt.string, + ['orchestrator.cluster.name']: rt.string, }) ); @@ -258,3 +259,21 @@ export const getServiceAssetsResponseRT = rt.type({ services: rt.array(assetRT), }); export type GetServiceAssetsResponse = rt.TypeOf; + +/** + * Pods + */ +export const getPodAssetsQueryOptionsRT = rt.intersection([ + rt.strict({ from: assetDateRT }), + rt.partial({ + to: assetDateRT, + size: sizeRT, + stringFilters: rt.string, + filters: assetFiltersSingleKindRT, + }), +]); +export type GetPodAssetsQueryOptions = rt.TypeOf; +export const getPodAssetsResponseRT = rt.type({ + pods: rt.array(assetRT), +}); +export type GetPodAssetsResponse = rt.TypeOf; diff --git a/x-pack/plugins/asset_manager/common/types_client.ts b/x-pack/plugins/asset_manager/common/types_client.ts index 40b3eb5e07e8f..5025bbdceed58 100644 --- a/x-pack/plugins/asset_manager/common/types_client.ts +++ b/x-pack/plugins/asset_manager/common/types_client.ts @@ -19,6 +19,7 @@ export interface SharedAssetsOptionsPublic { export type GetHostsOptionsPublic = SharedAssetsOptionsPublic; export type GetContainersOptionsPublic = SharedAssetsOptionsPublic; +export type GetPodsOptionsPublic = SharedAssetsOptionsPublic; export interface GetServicesOptionsPublic extends SharedAssetsOptionsPublic { diff --git a/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts b/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts index 7ff5617d8ceb5..2da23c359d4b9 100644 --- a/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts +++ b/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts @@ -10,13 +10,15 @@ import { GetContainersOptionsPublic, GetHostsOptionsPublic, GetServicesOptionsPublic, + GetPodsOptionsPublic, } from '../../common/types_client'; import { GetContainerAssetsResponse, GetHostAssetsResponse, GetServiceAssetsResponse, + GetPodAssetsResponse, } from '../../common/types_api'; -import { GET_CONTAINERS, GET_HOSTS, GET_SERVICES } from '../../common/constants_routes'; +import { GET_CONTAINERS, GET_HOSTS, GET_SERVICES, GET_PODS } from '../../common/constants_routes'; import { IPublicAssetsClient } from '../types'; export class PublicAssetsClient implements IPublicAssetsClient { @@ -57,4 +59,16 @@ export class PublicAssetsClient implements IPublicAssetsClient { return results; } + + async getPods(options: GetPodsOptionsPublic) { + const { filters, ...otherOptions } = options; + const results = await this.http.get(GET_PODS, { + query: { + stringFilters: JSON.stringify(filters), + ...otherOptions, + }, + }); + + return results; + } } diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/containers/get_containers.test.ts b/x-pack/plugins/asset_manager/server/lib/accessors/containers/get_containers.test.ts index 0a7b34b9ad1ad..8a7aad907a368 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/containers/get_containers.test.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/containers/get_containers.test.ts @@ -32,7 +32,7 @@ function createBaseOptions({ }; } -describe('getHosts', () => { +describe('getContainers', () => { let getApmIndicesMock = createGetApmIndicesMock(); let metricsDataClientMock = MetricsDataClientMock.create(); let baseOptions = createBaseOptions({ getApmIndicesMock, metricsDataClientMock }); diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.test.ts b/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.test.ts new file mode 100644 index 0000000000000..94d367963588c --- /dev/null +++ b/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.test.ts @@ -0,0 +1,341 @@ +/* + * Copyright 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 { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { GetApmIndicesMethod } from '../../asset_client_types'; +import { getPods } from './get_pods'; +import { + createGetApmIndicesMock, + expectToThrowValidationErrorWithStatusCode, +} from '../../../test_utils'; +import { MetricsDataClient, MetricsDataClientMock } from '@kbn/metrics-data-access-plugin/server'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; + +function createBaseOptions({ + getApmIndicesMock, + metricsDataClientMock, +}: { + getApmIndicesMock: GetApmIndicesMethod; + metricsDataClientMock: MetricsDataClient; +}) { + return { + sourceIndices: { + logs: 'my-logs*', + }, + getApmIndices: getApmIndicesMock, + metricsClient: metricsDataClientMock, + }; +} + +describe('getPods', () => { + let getApmIndicesMock = createGetApmIndicesMock(); + let metricsDataClientMock = MetricsDataClientMock.create(); + let baseOptions = createBaseOptions({ getApmIndicesMock, metricsDataClientMock }); + let esClientMock = elasticsearchClientMock.createScopedClusterClient().asCurrentUser; + let soClientMock = savedObjectsClientMock.create(); + + function resetMocks() { + getApmIndicesMock = createGetApmIndicesMock(); + metricsDataClientMock = MetricsDataClientMock.create(); + baseOptions = createBaseOptions({ getApmIndicesMock, metricsDataClientMock }); + esClientMock = elasticsearchClientMock.createScopedClusterClient().asCurrentUser; + soClientMock = savedObjectsClientMock.create(); + } + + beforeEach(() => { + resetMocks(); + + // ES returns no results, just enough structure to not blow up + esClientMock.search.mockResolvedValueOnce({ + took: 1, + timed_out: false, + _shards: { + failed: 0, + successful: 1, + total: 1, + }, + hits: { + hits: [], + }, + }); + }); + + it('should query Elasticsearch correctly', async () => { + await getPods({ + ...baseOptions, + from: 'now-5d', + to: 'now-3d', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + }); + + expect(metricsDataClientMock.getMetricIndices).toHaveBeenCalledTimes(1); + expect(metricsDataClientMock.getMetricIndices).toHaveBeenCalledWith({ + savedObjectsClient: soClientMock, + }); + + const dsl = esClientMock.search.mock.lastCall?.[0] as SearchRequest | undefined; + const { bool } = dsl?.query || {}; + expect(bool).toBeDefined(); + + expect(bool?.filter).toEqual([ + { + range: { + '@timestamp': { + gte: 'now-5d', + lte: 'now-3d', + }, + }, + }, + ]); + + expect(bool?.must).toEqual([ + { + exists: { + field: 'kubernetes.pod.uid', + }, + }, + { + exists: { + field: 'kubernetes.node.name', + }, + }, + ]); + }); + + it('should correctly include an EAN filter as a pod ID term query', async () => { + const mockPodId = '123abc'; + + await getPods({ + ...baseOptions, + from: 'now-1h', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + filters: { + ean: `pod:${mockPodId}`, + }, + }); + + const dsl = esClientMock.search.mock.lastCall?.[0] as SearchRequest | undefined; + const { bool } = dsl?.query || {}; + expect(bool).toBeDefined(); + + expect(bool?.must).toEqual( + expect.arrayContaining([ + { + exists: { + field: 'kubernetes.pod.uid', + }, + }, + { + exists: { + field: 'kubernetes.node.name', + }, + }, + { + term: { + 'kubernetes.pod.uid': mockPodId, + }, + }, + ]) + ); + }); + + it('should not query ES and return empty if filtering on non-pod EAN', async () => { + const mockId = 'some-id-123'; + + const result = await getPods({ + ...baseOptions, + from: 'now-1h', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + filters: { + ean: `container:${mockId}`, + }, + }); + + expect(esClientMock.search).toHaveBeenCalledTimes(0); + expect(result).toEqual({ pods: [] }); + }); + + it('should include a wildcard ID filter when an ID filter is provided with asterisks included', async () => { + const mockIdPattern = '*partial-id*'; + + await getPods({ + ...baseOptions, + from: 'now-1h', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + filters: { + id: mockIdPattern, + }, + }); + + const dsl = esClientMock.search.mock.lastCall?.[0] as SearchRequest | undefined; + const { bool } = dsl?.query || {}; + expect(bool).toBeDefined(); + + expect(bool?.must).toEqual( + expect.arrayContaining([ + { + exists: { + field: 'kubernetes.pod.uid', + }, + }, + { + exists: { + field: 'kubernetes.node.name', + }, + }, + { + wildcard: { + 'kubernetes.pod.uid': mockIdPattern, + }, + }, + ]) + ); + }); + + it('should include a term ID filter when an ID filter is provided without asterisks included', async () => { + const mockId = 'full-id'; + + await getPods({ + ...baseOptions, + from: 'now-1h', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + filters: { + id: mockId, + }, + }); + + const dsl = esClientMock.search.mock.lastCall?.[0] as SearchRequest | undefined; + const { bool } = dsl?.query || {}; + expect(bool).toBeDefined(); + + expect(bool?.must).toEqual( + expect.arrayContaining([ + { + exists: { + field: 'kubernetes.pod.uid', + }, + }, + { + exists: { + field: 'kubernetes.node.name', + }, + }, + { + term: { + 'kubernetes.pod.uid': mockId, + }, + }, + ]) + ); + }); + + it('should include a term filter for cloud filters', async () => { + const mockCloudProvider = 'gcp'; + const mockCloudRegion = 'us-central-1'; + + await getPods({ + ...baseOptions, + from: 'now-1h', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + filters: { + 'cloud.provider': mockCloudProvider, + 'cloud.region': mockCloudRegion, + }, + }); + + const dsl = esClientMock.search.mock.lastCall?.[0] as SearchRequest | undefined; + const { bool } = dsl?.query || {}; + expect(bool).toBeDefined(); + + expect(bool?.must).toEqual( + expect.arrayContaining([ + { + exists: { + field: 'kubernetes.pod.uid', + }, + }, + { + exists: { + field: 'kubernetes.node.name', + }, + }, + { + term: { + 'cloud.provider': mockCloudProvider, + }, + }, + { + term: { + 'cloud.region': mockCloudRegion, + }, + }, + ]) + ); + }); + + it('should reject with 400 for invalid "from" date', () => { + return expectToThrowValidationErrorWithStatusCode( + () => + getPods({ + ...baseOptions, + from: 'now-1zz', + to: 'now-3d', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + }), + { statusCode: 400 } + ); + }); + + it('should reject with 400 for invalid "to" date', () => { + return expectToThrowValidationErrorWithStatusCode( + () => + getPods({ + ...baseOptions, + from: 'now-5d', + to: 'now-3fe', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + }), + { statusCode: 400 } + ); + }); + + it('should reject with 400 when "from" is a date that is after "to"', () => { + return expectToThrowValidationErrorWithStatusCode( + () => + getPods({ + ...baseOptions, + from: 'now', + to: 'now-5d', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + }), + { statusCode: 400 } + ); + }); + + it('should reject with 400 when "from" is in the future', () => { + return expectToThrowValidationErrorWithStatusCode( + () => + getPods({ + ...baseOptions, + from: 'now+1d', + elasticsearchClient: esClientMock, + savedObjectsClient: soClientMock, + }), + { statusCode: 400 } + ); + }); +}); diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.ts b/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.ts new file mode 100644 index 0000000000000..db2bc11ae2315 --- /dev/null +++ b/x-pack/plugins/asset_manager/server/lib/accessors/pods/get_pods.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 { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { Asset } from '../../../../common/types_api'; +import { GetPodsOptionsPublic } from '../../../../common/types_client'; +import { + AssetClientDependencies, + AssetClientOptionsWithInjectedValues, +} from '../../asset_client_types'; +import { parseEan } from '../../parse_ean'; +import { collectPods } from '../../collectors/pods'; +import { validateStringDateRange } from '../../validators/validate_date_range'; + +export type GetPodsOptions = GetPodsOptionsPublic & AssetClientDependencies; +export type GetPodsOptionsInjected = AssetClientOptionsWithInjectedValues; + +export async function getPods(options: GetPodsOptionsInjected): Promise<{ pods: Asset[] }> { + validateStringDateRange(options.from, options.to); + + const metricsIndices = await options.metricsClient.getMetricIndices({ + savedObjectsClient: options.savedObjectsClient, + }); + + const filters: QueryDslQueryContainer[] = []; + + if (options.filters?.ean) { + const ean = Array.isArray(options.filters.ean) ? options.filters.ean[0] : options.filters.ean; + const { kind, id } = parseEan(ean); + + // if EAN filter isn't targeting a pod asset, we don't need to do this query + if (kind !== 'pod') { + return { + pods: [], + }; + } + + filters.push({ + term: { + 'kubernetes.pod.uid': id, + }, + }); + } + + if (options.filters?.id) { + const fn = options.filters.id.includes('*') ? 'wildcard' : 'term'; + filters.push({ + [fn]: { + 'kubernetes.pod.uid': options.filters.id, + }, + }); + } + + if (options.filters?.['orchestrator.cluster.name']) { + filters.push({ + term: { + 'orchestrator.cluster.name': options.filters['orchestrator.cluster.name'], + }, + }); + } + + if (options.filters?.['cloud.provider']) { + filters.push({ + term: { + 'cloud.provider': options.filters['cloud.provider'], + }, + }); + } + + if (options.filters?.['cloud.region']) { + filters.push({ + term: { + 'cloud.region': options.filters['cloud.region'], + }, + }); + } + + const { assets } = await collectPods({ + client: options.elasticsearchClient, + from: options.from, + to: options.to || 'now', + filters, + sourceIndices: { + metrics: metricsIndices, + logs: options.sourceIndices.logs, + }, + }); + + return { + pods: assets, + }; +} diff --git a/x-pack/plugins/asset_manager/server/lib/asset_client.ts b/x-pack/plugins/asset_manager/server/lib/asset_client.ts index a7aad8f6a01c4..ca6e7f2ea05d2 100644 --- a/x-pack/plugins/asset_manager/server/lib/asset_client.ts +++ b/x-pack/plugins/asset_manager/server/lib/asset_client.ts @@ -9,6 +9,7 @@ import { Asset } from '../../common/types_api'; import { getContainers, GetContainersOptions } from './accessors/containers/get_containers'; import { getHosts, GetHostsOptions } from './accessors/hosts/get_hosts'; import { getServices, GetServicesOptions } from './accessors/services/get_services'; +import { getPods, GetPodsOptions } from './accessors/pods/get_pods'; import { AssetClientBaseOptions, AssetClientOptionsWithInjectedValues } from './asset_client_types'; export class AssetClient { @@ -35,4 +36,9 @@ export class AssetClient { const withInjected = this.injectOptions(options); return await getContainers(withInjected); } + + async getPods(options: GetPodsOptions): Promise<{ pods: Asset[] }> { + const withInjected = this.injectOptions(options); + return await getPods(withInjected); + } } diff --git a/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts b/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts index 5cbf357e69569..f9a4e2e22ae51 100644 --- a/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts +++ b/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts @@ -9,11 +9,24 @@ import { estypes } from '@elastic/elasticsearch'; import { Asset } from '../../../common/types_api'; import { CollectorOptions, QUERY_MAX_SIZE } from '.'; -export async function collectPods({ client, from, to, sourceIndices, afterKey }: CollectorOptions) { +export async function collectPods({ + client, + from, + to, + sourceIndices, + filters = [], + afterKey, +}: CollectorOptions) { if (!sourceIndices?.metrics || !sourceIndices?.logs) { throw new Error('missing required metrics/logs indices'); } + const musts = [ + ...filters, + { exists: { field: 'kubernetes.pod.uid' } }, + { exists: { field: 'kubernetes.node.name' } }, + ]; + const { metrics, logs } = sourceIndices; const dsl: estypes.SearchRequest = { index: [metrics, logs], @@ -42,10 +55,7 @@ export async function collectPods({ client, from, to, sourceIndices, afterKey }: }, }, ], - must: [ - { exists: { field: 'kubernetes.pod.uid' } }, - { exists: { field: 'kubernetes.node.name' } }, - ], + must: musts, }, }, }; diff --git a/x-pack/plugins/asset_manager/server/routes/assets/pods.ts b/x-pack/plugins/asset_manager/server/routes/assets/pods.ts new file mode 100644 index 0000000000000..beed936bd5b40 --- /dev/null +++ b/x-pack/plugins/asset_manager/server/routes/assets/pods.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createRouteValidationFunction } from '@kbn/io-ts-utils'; +import { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import { GetPodAssetsQueryOptions, getPodAssetsQueryOptionsRT } from '../../../common/types_api'; +import { debug } from '../../../common/debug_log'; +import { SetupRouteOptions } from '../types'; +import * as routePaths from '../../../common/constants_routes'; +import { getClientsFromContext, validateStringAssetFilters } from '../utils'; +import { AssetsValidationError } from '../../lib/validators/validation_error'; + +export function podsRoutes({ + router, + assetClient, +}: SetupRouteOptions) { + const validate = createRouteValidationFunction(getPodAssetsQueryOptionsRT); + router.get( + { + path: routePaths.GET_PODS, + validate: { + query: (q, res) => { + const [invalidResponse, validatedFilters] = validateStringAssetFilters(q, res); + if (invalidResponse) { + return invalidResponse; + } + if (validatedFilters) { + q.filters = validatedFilters; + } + return validate(q, res); + }, + }, + }, + async (context, req, res) => { + const { from = 'now-24h', to = 'now', filters } = req.query || {}; + const { elasticsearchClient, savedObjectsClient } = await getClientsFromContext(context); + + try { + const response = await assetClient.getPods({ + from, + to, + filters, + elasticsearchClient, + savedObjectsClient, + }); + + return res.ok({ body: response }); + } catch (error: unknown) { + debug('Error while looking up POD asset records', error); + + if (error instanceof AssetsValidationError) { + return res.customError({ + statusCode: error.statusCode, + body: { + message: `Error while looking up pod asset records - ${error.message}`, + }, + }); + } + return res.customError({ + statusCode: 500, + body: { message: 'Error while looking up pod asset records - ' + `${error}` }, + }); + } + } + ); +} diff --git a/x-pack/plugins/asset_manager/server/routes/index.ts b/x-pack/plugins/asset_manager/server/routes/index.ts index 20f5abf889e78..991c96806b767 100644 --- a/x-pack/plugins/asset_manager/server/routes/index.ts +++ b/x-pack/plugins/asset_manager/server/routes/index.ts @@ -12,6 +12,7 @@ import { sampleAssetsRoutes } from './sample_assets'; import { hostsRoutes } from './assets/hosts'; import { servicesRoutes } from './assets/services'; import { containersRoutes } from './assets/containers'; +import { podsRoutes } from './assets/pods'; export function setupRoutes({ router, @@ -22,4 +23,5 @@ export function setupRoutes({ hostsRoutes({ router, assetClient }); servicesRoutes({ router, assetClient }); containersRoutes({ router, assetClient }); + podsRoutes({ router, assetClient }); } diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx index 915676b8f6b5c..f12a805cf18a4 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx @@ -70,8 +70,10 @@ const testVisTypes: BaseVisType[] = [ const testVisTypeAliases: VisTypeAlias[] = [ { title: 'Lens', - aliasApp: 'lens', - aliasPath: 'path/to/lens', + alias: { + app: 'lens', + path: 'path/to/lens', + }, icon: 'lensApp', name: 'lens', description: 'Description of Lens app', @@ -79,8 +81,10 @@ const testVisTypeAliases: VisTypeAlias[] = [ }, { title: 'Maps', - aliasApp: 'maps', - aliasPath: 'path/to/maps', + alias: { + app: 'maps', + path: 'path/to/maps', + }, icon: 'gisApp', name: 'maps', description: 'Description of Maps app', diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx index dbcd3b9cd2786..d405a977affd3 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx @@ -68,12 +68,14 @@ export const EditorMenu: FC = ({ addElement }) => { trackCanvasUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); } - if ('aliasPath' in visType) { - appId = visType.aliasApp; - path = visType.aliasPath; - } else { + if (!('alias' in visType)) { + // this visualization is not an alias appId = 'visualize'; path = `#/create?type=${encodeURIComponent(visType.name)}`; + } else if (visType.alias && 'path' in visType.alias) { + // this visualization **is** an alias, and it has an app to redirect to for creation + appId = visType.alias.app; + path = visType.alias.path; } } else { appId = 'visualize'; @@ -134,7 +136,8 @@ export const EditorMenu: FC = ({ addElement }) => { .getAliases() .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => a === b ? 0 : a ? -1 : 1 - ); + ) + .filter(({ disableCreate }: VisTypeAlias) => !disableCreate); const factories = unwrappedEmbeddableFactories .filter( diff --git a/x-pack/plugins/cases/common/constants/index.ts b/x-pack/plugins/cases/common/constants/index.ts index 5a540b610135c..d4b6839e1dc37 100644 --- a/x-pack/plugins/cases/common/constants/index.ts +++ b/x-pack/plugins/cases/common/constants/index.ts @@ -162,6 +162,7 @@ export const READ_CASES_CAPABILITY = 'read_cases' as const; export const UPDATE_CASES_CAPABILITY = 'update_cases' as const; export const DELETE_CASES_CAPABILITY = 'delete_cases' as const; export const PUSH_CASES_CAPABILITY = 'push_cases' as const; +export const CASES_SETTINGS_CAPABILITY = 'cases_settings' as const; export const CASES_CONNECTORS_CAPABILITY = 'cases_connectors' as const; /** diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index 4283adf4c081a..520b5ca079b63 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -29,6 +29,7 @@ export type { Ecs, CaseViewRefreshPropInterface, CasesPermissions, + CasesCapabilities, CasesStatus, } from './ui/types'; @@ -52,6 +53,7 @@ export { CASE_COMMENT_SAVED_OBJECT, CASES_CONNECTORS_CAPABILITY, GET_CONNECTORS_CONFIGURE_API_TAG, + CASES_SETTINGS_CAPABILITY, } from './constants'; export type { AttachmentAttributes } from './types/domain'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 2a76e56a59fe0..01d006a0dcd7d 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -12,7 +12,11 @@ import type { READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, } from '..'; -import type { CASES_CONNECTORS_CAPABILITY, PUSH_CASES_CAPABILITY } from '../constants'; +import type { + CASES_CONNECTORS_CAPABILITY, + CASES_SETTINGS_CAPABILITY, + PUSH_CASES_CAPABILITY, +} from '../constants'; import type { SnakeToCamelCase } from '../types'; import type { CaseSeverity, @@ -299,6 +303,7 @@ export interface CasesPermissions { delete: boolean; push: boolean; connectors: boolean; + settings: boolean; } export interface CasesCapabilities { @@ -308,4 +313,5 @@ export interface CasesCapabilities { [DELETE_CASES_CAPABILITY]: boolean; [PUSH_CASES_CAPABILITY]: boolean; [CASES_CONNECTORS_CAPABILITY]: boolean; + [CASES_SETTINGS_CAPABILITY]: boolean; } diff --git a/x-pack/plugins/cases/common/utils/capabilities.test.tsx b/x-pack/plugins/cases/common/utils/capabilities.test.tsx new file mode 100644 index 0000000000000..07b82ea0d0e8f --- /dev/null +++ b/x-pack/plugins/cases/common/utils/capabilities.test.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 { createUICapabilities } from './capabilities'; + +describe('createUICapabilities', () => { + it('returns the UI capabilities correctly', () => { + expect(createUICapabilities()).toMatchInlineSnapshot(` + Object { + "all": Array [ + "create_cases", + "read_cases", + "update_cases", + "push_cases", + "cases_connectors", + ], + "delete": Array [ + "delete_cases", + ], + "read": Array [ + "read_cases", + "cases_connectors", + ], + "settings": Array [ + "cases_settings", + ], + } + `); + }); +}); diff --git a/x-pack/plugins/cases/common/utils/capabilities.ts b/x-pack/plugins/cases/common/utils/capabilities.ts index 28b3fa00f9272..6b33dd8c8dceb 100644 --- a/x-pack/plugins/cases/common/utils/capabilities.ts +++ b/x-pack/plugins/cases/common/utils/capabilities.ts @@ -12,12 +12,14 @@ import { PUSH_CASES_CAPABILITY, READ_CASES_CAPABILITY, UPDATE_CASES_CAPABILITY, + CASES_SETTINGS_CAPABILITY, } from '../constants'; export interface CasesUiCapabilities { all: readonly string[]; read: readonly string[]; delete: readonly string[]; + settings: readonly string[]; } /** * Return the UI capabilities for each type of operation. These strings must match the values defined in the UI @@ -33,4 +35,5 @@ export const createUICapabilities = (): CasesUiCapabilities => ({ ] as const, read: [READ_CASES_CAPABILITY, CASES_CONNECTORS_CAPABILITY] as const, delete: [DELETE_CASES_CAPABILITY] as const, + settings: [CASES_SETTINGS_CAPABILITY] as const, }); diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json index 54a9c31d34312..bf6b28c8441a9 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.json +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -1,9 +1,9 @@ { - "openapi": "3.0.1", + "openapi": "3.1.0", "info": { "title": "Cases", "description": "OpenAPI schema for Cases endpoints", - "version": "0.1", + "version": "0.2", "contact": { "name": "Cases Team" }, @@ -329,7 +329,7 @@ } } }, - "example": [ + "examples": [ { "id": "06116b80-e1c3-11ec-be9b-9b1838238ee6", "title": "security_case" @@ -417,18 +417,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -438,7 +444,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -449,34 +457,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -485,15 +513,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -502,13 +536,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -516,30 +557,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } } @@ -640,18 +696,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -661,7 +723,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -672,34 +736,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -708,15 +792,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -725,13 +815,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -739,30 +836,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } }, @@ -867,18 +979,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -888,7 +1006,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -899,34 +1019,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -935,15 +1075,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -952,13 +1098,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -966,30 +1119,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } }, @@ -1044,23 +1212,37 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } } @@ -1569,8 +1751,10 @@ "content": { "application/json": { "schema": { - "type": "object", - "nullable": true + "type": [ + "object", + "null" + ] } } } @@ -2103,7 +2287,7 @@ } } }, - "example": [ + "examples": [ { "id": "06116b80-e1c3-11ec-be9b-9b1838238ee6", "title": "security_case" @@ -2194,18 +2378,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -2215,7 +2405,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -2226,34 +2418,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -2262,15 +2474,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -2279,13 +2497,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -2293,30 +2518,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } } @@ -2420,18 +2660,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -2441,7 +2687,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -2452,34 +2700,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -2488,15 +2756,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -2505,13 +2779,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -2519,30 +2800,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } }, @@ -2650,18 +2946,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -2671,7 +2973,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-06-01T17:07:17.767Z" + "examples": [ + "2022-06-01T17:07:17.767Z" + ] }, "created_by": { "type": "object", @@ -2682,34 +2986,54 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "error": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "type": "string", - "example": "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + "examples": [ + "4a97a440-e1cd-11ec-be9b-9b1838238ee6" + ] }, "mappings": { "type": "array", @@ -2718,15 +3042,21 @@ "properties": { "action_type": { "type": "string", - "example": "overwrite" + "examples": [ + "overwrite" + ] }, "source": { "type": "string", - "example": "title" + "examples": [ + "title" + ] }, "target": { "type": "string", - "example": "summary" + "examples": [ + "summary" + ] } } } @@ -2735,13 +3065,20 @@ "$ref": "#/components/schemas/owners" }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": "2022-06-01T19:58:48.169Z" + "examples": [ + "2022-06-01T19:58:48.169Z" + ] }, "updated_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -2749,30 +3086,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "version": { "type": "string", - "example": "WzIwNzMsMV0=" + "examples": [ + "WzIwNzMsMV0=" + ] } } }, @@ -2909,23 +3261,37 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } } @@ -3515,8 +3881,10 @@ "content": { "application/json": { "schema": { - "type": "object", - "nullable": true + "type": [ + "object", + "null" + ] } } } @@ -3610,8 +3978,7 @@ "$ref": "#/components/parameters/space_id" }, { - "$ref": "#/components/parameters/page_index", - "example": "1" + "$ref": "#/components/parameters/page_index" }, { "$ref": "#/components/parameters/page_size" @@ -3679,7 +4046,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "parameters": { @@ -3703,9 +4071,13 @@ "type": "string", "minItems": 1, "maxItems": 100 - } - }, - "example": "d4e7abb0-b462-11ec-9a8d-698504725a43" + }, + "examples": [ + [ + "d4e7abb0-b462-11ec-9a8d-698504725a43" + ] + ] + } }, "assignees": { "in": "query", @@ -3740,11 +4112,15 @@ "items": { "type": "string" }, - "maxItems": 100 + "maxItems": 100, + "examples": [ + [ + "my-category" + ] + ] } ] - }, - "example": "my-category" + } }, "defaultSearchOperator": { "in": "query", @@ -3752,9 +4128,11 @@ "description": "he default operator to use for the simple_query_string.", "schema": { "type": "string", - "default": "OR" - }, - "example": "OR" + "default": "OR", + "examples": [ + "OR" + ] + } }, "from": { "in": "query", @@ -3762,7 +4140,9 @@ "description": "Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression.\n", "schema": { "type": "string", - "example": "now-1d" + "examples": [ + "now-1d" + ] } }, "owner": { @@ -3778,11 +4158,15 @@ "type": "array", "items": { "$ref": "#/components/schemas/owners" - } + }, + "examples": [ + [ + "cases" + ] + ] } ] - }, - "example": "cases" + } }, "page_index": { "in": "query", @@ -3791,7 +4175,10 @@ "required": false, "schema": { "type": "integer", - "default": 1 + "default": 1, + "examples": [ + 1 + ] } }, "page_size": { @@ -3819,11 +4206,15 @@ "items": { "type": "string" }, - "maxItems": 100 + "maxItems": 100, + "examples": [ + [ + "elastic" + ] + ] } ] - }, - "example": "elastic" + } }, "search": { "in": "query", @@ -3880,9 +4271,11 @@ "status", "severity" ], - "default": "createdAt" - }, - "example": "updatedAt" + "default": "createdAt", + "examples": [ + "updatedAt" + ] + } }, "sort_order": { "in": "query", @@ -3908,9 +4301,11 @@ "closed", "in-progress", "open" + ], + "examples": [ + "open" ] - }, - "example": "open" + } }, "tags": { "in": "query", @@ -3926,20 +4321,26 @@ "items": { "type": "string" }, - "maxItems": 100 + "maxItems": 100, + "examples": [ + [ + "tag-1" + ] + ] } ] - }, - "example": "tag-1" + } }, "to": { "in": "query", "name": "to", "description": "Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression.\n", "schema": { - "type": "string" - }, - "example": "now+1d" + "type": "string", + "examples": [ + "now+1d" + ] + } }, "alert_id": { "in": "path", @@ -3948,7 +4349,9 @@ "required": true, "schema": { "type": "string", - "example": "09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540" + "examples": [ + "09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540" + ] } }, "configuration_id": { @@ -3958,7 +4361,9 @@ "required": true, "schema": { "type": "string", - "example": "3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9" + "examples": [ + "3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9" + ] } }, "case_id": { @@ -3968,7 +4373,9 @@ "required": true, "schema": { "type": "string", - "example": "9c235210-6834-11ea-a78c-6ffb38a34414" + "examples": [ + "9c235210-6834-11ea-a78c-6ffb38a34414" + ] } }, "includeComments": { @@ -3988,7 +4395,9 @@ "required": true, "schema": { "type": "string", - "example": "71ec1870-725b-11ea-a0b2-c51ea50a58e2" + "examples": [ + "71ec1870-725b-11ea-a0b2-c51ea50a58e2" + ] } }, "connector_id": { @@ -3998,7 +4407,9 @@ "required": true, "schema": { "type": "string", - "example": "abed3a70-71bd-11ea-a0b2-c51ea50a58e2" + "examples": [ + "abed3a70-71bd-11ea-a0b2-c51ea50a58e2" + ] } }, "user_action_types": { @@ -4026,9 +4437,13 @@ "title", "user" ] - } - }, - "example": "create_case" + }, + "examples": [ + [ + "create_case" + ] + ] + } }, "space_id": { "in": "path", @@ -4037,16 +4452,20 @@ "required": true, "schema": { "type": "string", - "example": "default" + "examples": [ + "default" + ] } } }, "schemas": { "assignees": { - "type": "array", + "type": [ + "array", + "null" + ], "description": "An array containing users that are assigned to the case.", "maxItems": 10, - "nullable": true, "items": { "type": "object", "required": [ @@ -4056,7 +4475,9 @@ "uid": { "type": "string", "description": "A unique identifier for the user profile. These identifiers can be found by using the suggest user profile API.", - "example": "u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0" + "examples": [ + "u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0" + ] } } } @@ -4074,24 +4495,34 @@ "properties": { "fields": { "description": "An object containing the connector fields. To create a case without a connector, specify null. To update a case to remove the connector, specify null.", - "nullable": true, - "type": "string", - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "description": "The identifier for the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "description": "The type of connector. To create a case without a connector, use `.none`. To update a case to remove the connector, specify `.none`.", "type": "string", - "example": ".none", + "examples": [ + ".none" + ], "enum": [ ".none" ] @@ -4110,9 +4541,13 @@ "type": "object", "properties": { "fields": { - "type": "string", - "nullable": true, - "example": null + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "id": { "description": "The identifier for the connector. To retrieve connector IDs, use the find connectors API.", @@ -4125,7 +4560,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".cases-webhook", + "examples": [ + ".cases-webhook" + ], "enum": [ ".cases-webhook" ] @@ -4154,18 +4591,24 @@ "properties": { "issueType": { "description": "The type of issue.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "parent": { "description": "The key of the parent issue, when the issue type is sub-task.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "priority": { "description": "The priority of the issue.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -4180,7 +4623,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".jira", + "examples": [ + ".jira" + ], "enum": [ ".jira" ] @@ -4200,8 +4645,10 @@ "properties": { "fields": { "description": "An object containing the connector fields. If you want to omit any individual field, specify null as its value.", - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "required": [ "issueTypes", "severityCode" @@ -4231,7 +4678,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".resilient", + "examples": [ + ".resilient" + ], "enum": [ ".resilient" ] @@ -4262,28 +4711,38 @@ "properties": { "category": { "description": "The category of the incident.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "impact": { "description": "The effect an incident had on business.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "severity": { "description": "The severity of the incident.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "subcategory": { "description": "The subcategory of the incident.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "urgency": { "description": "The extent to which the incident resolution can be delayed.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -4298,7 +4757,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".servicenow", + "examples": [ + ".servicenow" + ], "enum": [ ".servicenow" ] @@ -4331,38 +4792,52 @@ "properties": { "category": { "description": "The category of the incident.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "destIp": { "description": "Indicates whether cases will send a comma-separated list of destination IPs.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "malwareHash": { "description": "Indicates whether cases will send a comma-separated list of malware hashes.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "malwareUrl": { "description": "Indicates whether cases will send a comma-separated list of malware URLs.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "priority": { "description": "The priority of the issue.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] }, "sourceIp": { "description": "Indicates whether cases will send a comma-separated list of source IPs.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "subcategory": { "description": "The subcategory of the incident.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -4377,7 +4852,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".servicenow-sir", + "examples": [ + ".servicenow-sir" + ], "enum": [ ".servicenow-sir" ] @@ -4404,8 +4881,10 @@ "properties": { "caseId": { "description": "The case identifier for Swimlane connectors.", - "type": "string", - "nullable": true + "type": [ + "string", + "null" + ] } } }, @@ -4420,7 +4899,9 @@ "type": { "description": "The type of connector.", "type": "string", - "example": ".swimlane", + "examples": [ + ".swimlane" + ], "enum": [ ".swimlane" ] @@ -4435,7 +4916,9 @@ "observability", "securitySolution" ], - "example": "cases" + "examples": [ + "cases" + ] }, "settings": { "type": "object", @@ -4447,7 +4930,9 @@ "syncAlerts": { "description": "Turns alert syncing on or off.", "type": "boolean", - "example": true + "examples": [ + true + ] } } }, @@ -4566,10 +5051,12 @@ "description": "The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`.\n", "oneOf": [ { - "type": "string", + "type": [ + "string", + "null" + ], "minLength": 1, - "maxLength": 160, - "nullable": true + "maxLength": 160 }, { "type": "boolean" @@ -4583,27 +5070,43 @@ }, "case_response_closed_by_properties": { "title": "Case response properties for closed_by", - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -4623,13 +5126,17 @@ "type": "array", "items": { "type": "string", - "example": "a6e12ac4-7bce-457b-84f6-d7ce8deb8446" + "examples": [ + "a6e12ac4-7bce-457b-84f6-d7ce8deb8446" + ] } }, "created_at": { "type": "string", "format": "date-time", - "example": "2023-11-06T19:29:38.424Z" + "examples": [ + "2023-11-06T19:29:38.424Z" + ] }, "created_by": { "type": "object", @@ -4640,48 +5147,73 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "id": { "type": "string", - "example": "73362370-ab1a-11ec-985f-97e55adae8b9" + "examples": [ + "73362370-ab1a-11ec-985f-97e55adae8b9" + ] }, "index": { "type": "array", "items": { "type": "string", - "example": ".internal.alerts-security.alerts-default-000001" + "examples": [ + ".internal.alerts-security.alerts-default-000001" + ] } }, "owner": { "$ref": "#/components/schemas/owners" }, "pushed_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "example": null, - "nullable": true + "examples": [ + null + ] }, "pushed_by": { - "type": "object", + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -4689,26 +5221,39 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } }, "rule": { "type": "object", @@ -4716,30 +5261,40 @@ "id": { "description": "The rule identifier.", "type": "string", - "example": "94d80550-aaf4-11ec-985f-97e55adae8b9" + "examples": [ + "94d80550-aaf4-11ec-985f-97e55adae8b9" + ] }, "name": { "description": "The rule name.", "type": "string", - "example": "security_rule" + "examples": [ + "security_rule" + ] } } }, "type": { "type": "string", - "example": "alert", + "examples": [ + "alert" + ], "enum": [ "alert" ] }, "updated_at": { - "type": "string", - "format": "date-time", - "nullable": true + "type": [ + "string", + "null" + ], + "format": "date-time" }, "updated_by": { - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "required": [ "email", "full_name", @@ -4747,29 +5302,45 @@ ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } } }, "version": { "type": "string", - "example": "WzMwNDgsMV0=" + "examples": [ + "WzMwNDgsMV0=" + ] } } }, @@ -4778,23 +5349,37 @@ "type": "object", "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -4805,27 +5390,43 @@ }, "case_response_pushed_by_properties": { "title": "Case response properties for pushed_by", - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -4836,27 +5437,43 @@ }, "case_response_updated_by_properties": { "title": "Case response properties for updated_by", - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -4874,57 +5491,77 @@ "properties": { "comment": { "type": "string", - "example": "A new comment." + "examples": [ + "A new comment." + ] }, "created_at": { "type": "string", "format": "date-time", - "example": "2022-05-13T09:16:17.416Z" + "examples": [ + "2022-05-13T09:16:17.416Z" + ] }, "created_by": { "$ref": "#/components/schemas/case_response_created_by_properties" }, "id": { "type": "string", - "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + "examples": [ + "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + ] }, "owner": { "$ref": "#/components/schemas/owners" }, "pushed_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": null + "examples": [ + null + ] }, "pushed_by": { "$ref": "#/components/schemas/case_response_pushed_by_properties" }, "type": { "type": "string", - "example": "user", + "examples": [ + "user" + ], "enum": [ "user" ] }, "updated_at": { - "type": "string", + "type": [ + "string", + "null" + ], "format": "date-time", - "nullable": true, - "example": null + "examples": [ + null + ] }, "updated_by": { "$ref": "#/components/schemas/case_response_updated_by_properties" }, "version": { "type": "string", - "example": "WzIwNDMxLDFd" + "examples": [ + "WzIwNDMxLDFd" + ] } } }, "external_service": { - "type": "object", - "nullable": true, + "type": [ + "object", + "null" + ], "properties": { "connector_id": { "type": "string" @@ -4946,29 +5583,45 @@ "format": "date-time" }, "pushed_by": { - "type": "object", + "type": [ + "object", + "null" + ], "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } - }, - "nullable": true + } } } }, @@ -5012,14 +5665,18 @@ "$ref": "#/components/schemas/assignees" }, "category": { - "type": "string", - "description": "The case category.", - "nullable": true + "type": [ + "string", + "null" + ], + "description": "The case category." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true + "type": [ + "string", + "null" + ], + "format": "date-time" }, "closed_by": { "$ref": "#/components/schemas/case_response_closed_by_properties" @@ -5075,7 +5732,9 @@ "created_at": { "type": "string", "format": "date-time", - "example": "2022-05-13T09:16:17.416Z" + "examples": [ + "2022-05-13T09:16:17.416Z" + ] }, "created_by": { "$ref": "#/components/schemas/case_response_created_by_properties" @@ -5103,10 +5762,12 @@ "description": "The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`.\n", "oneOf": [ { - "type": "string", + "type": [ + "string", + "null" + ], "minLength": 1, - "maxLength": 160, - "nullable": true + "maxLength": 160 }, { "type": "boolean" @@ -5118,20 +5779,28 @@ }, "description": { "type": "string", - "example": "A case description." + "examples": [ + "A case description." + ] }, "duration": { - "type": "integer", + "type": [ + "integer", + "null" + ], "description": "The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero.\n", - "nullable": true, - "example": 120 + "examples": [ + 120 + ] }, "external_service": { "$ref": "#/components/schemas/external_service" }, "id": { "type": "string", - "example": "66b9aa00-94fa-11ea-9f74-e7e108796192" + "examples": [ + "66b9aa00-94fa-11ea-9f74-e7e108796192" + ] }, "owner": { "$ref": "#/components/schemas/owners" @@ -5150,33 +5819,45 @@ "items": { "type": "string" }, - "example": [ - "tag-1" + "examples": [ + [ + "tag-1" + ] ] }, "title": { "type": "string", - "example": "Case title 1" + "examples": [ + "Case title 1" + ] }, "totalAlerts": { "type": "integer", - "example": 0 + "examples": [ + 0 + ] }, "totalComment": { "type": "integer", - "example": 0 + "examples": [ + 0 + ] }, "updated_at": { - "type": "string", - "format": "date-time", - "nullable": true + "type": [ + "string", + "null" + ], + "format": "date-time" }, "updated_by": { "$ref": "#/components/schemas/case_response_updated_by_properties" }, "version": { "type": "string", - "example": "WzUzMiwxXQ==" + "examples": [ + "WzUzMiwxXQ==" + ] } } }, @@ -5186,14 +5867,18 @@ "properties": { "error": { "type": "string", - "example": "Unauthorized" + "examples": [ + "Unauthorized" + ] }, "message": { "type": "string" }, "statusCode": { "type": "integer", - "example": 401 + "examples": [ + 401 + ] } } }, @@ -5280,10 +5965,12 @@ "description": "The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`.\n", "oneOf": [ { - "type": "string", + "type": [ + "string", + "null" + ], "minLength": 1, - "maxLength": 160, - "nullable": true + "maxLength": 160 }, { "type": "boolean" @@ -5349,7 +6036,9 @@ "close-by-pushing", "close-by-user" ], - "example": "close-by-user" + "examples": [ + "close-by-user" + ] }, "connector_types": { "type": "string", @@ -5363,7 +6052,9 @@ ".servicenow-sir", ".swimlane" ], - "example": ".none" + "examples": [ + ".none" + ] }, "set_case_configuration_request": { "title": "Set case configuration request", @@ -5384,18 +6075,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -5472,18 +6169,24 @@ "properties": { "fields": { "description": "The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`.", - "nullable": true, - "type": "object" + "type": [ + "object", + "null" + ] }, "id": { "description": "The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -5539,7 +6242,9 @@ "version": { "description": "The version of the connector. To retrieve the version value, use the get configuration API.\n", "type": "string", - "example": "WzIwMiwxXQ==" + "examples": [ + "WzIwMiwxXQ==" + ] } } }, @@ -5576,7 +6281,9 @@ } ], "x-technical-preview": true, - "example": "6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42" + "examples": [ + "6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42" + ] }, "alert_indices": { "title": "Alert indices", @@ -5604,12 +6311,16 @@ "id": { "description": "The rule identifier.", "type": "string", - "example": "94d80550-aaf4-11ec-985f-97e55adae8b9" + "examples": [ + "94d80550-aaf4-11ec-985f-97e55adae8b9" + ] }, "name": { "description": "The rule name.", "type": "string", - "example": "security_rule" + "examples": [ + "security_rule" + ] } } }, @@ -5640,7 +6351,9 @@ "type": { "description": "The type of comment.", "type": "string", - "example": "alert", + "examples": [ + "alert" + ], "enum": [ "alert" ] @@ -5656,7 +6369,9 @@ "description": "The new comment. It is required only when `type` is `user`.", "type": "string", "maxLength": 30000, - "example": "A new comment." + "examples": [ + "A new comment." + ] }, "owner": { "$ref": "#/components/schemas/owners" @@ -5664,7 +6379,9 @@ "type": { "type": "string", "description": "The type of comment.", - "example": "user", + "examples": [ + "user" + ], "enum": [ "user" ] @@ -5711,7 +6428,9 @@ "id": { "type": "string", "description": "The identifier for the comment. To retrieve comment IDs, use the get comments API.\n", - "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + "examples": [ + "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + ] }, "index": { "$ref": "#/components/schemas/alert_indices" @@ -5728,12 +6447,16 @@ "enum": [ "alert" ], - "example": "alert" + "examples": [ + "alert" + ] }, "version": { "description": "The current comment version. To retrieve version values, use the get comments API.\n", "type": "string", - "example": "Wzk1LDFd" + "examples": [ + "Wzk1LDFd" + ] } } }, @@ -5746,12 +6469,16 @@ "description": "The new comment. It is required only when `type` is `user`.", "type": "string", "maxLength": 30000, - "example": "A new comment." + "examples": [ + "A new comment." + ] }, "id": { "type": "string", "description": "The identifier for the comment. To retrieve comment IDs, use the get comments API.\n", - "example": "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + "examples": [ + "8af6ac20-74f6-11ea-b83a-553aecdb28b6" + ] }, "owner": { "$ref": "#/components/schemas/owners" @@ -5762,12 +6489,16 @@ "enum": [ "user" ], - "example": "user" + "examples": [ + "user" + ] }, "version": { "description": "The current comment version. To retrieve version values, use the get comments API.\n", "type": "string", - "example": "Wzk1LDFd" + "examples": [ + "Wzk1LDFd" + ] } }, "required": [ @@ -5802,7 +6533,9 @@ "push_to_service", "update" ], - "example": "create" + "examples": [ + "create" + ] }, "payload_alert_comment": { "type": "object", @@ -5814,7 +6547,9 @@ "oneOf": [ { "type": "string", - "example": "1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d" + "examples": [ + "1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d" + ] }, { "type": "array", @@ -5828,7 +6563,9 @@ "oneOf": [ { "type": "string", - "example": ".alerts-observability.logs.alerts-default" + "examples": [ + ".alerts-observability.logs.alerts-default" + ] }, { "type": "array", @@ -5847,12 +6584,16 @@ "id": { "description": "The rule identifier.", "type": "string", - "example": "94d80550-aaf4-11ec-985f-97e55adae8b9" + "examples": [ + "94d80550-aaf4-11ec-985f-97e55adae8b9" + ] }, "name": { "description": "The rule name.", "type": "string", - "example": "security_rule" + "examples": [ + "security_rule" + ] } } }, @@ -5881,9 +6622,11 @@ "type": "object", "properties": { "fields": { - "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", - "nullable": true, - "type": "object", + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.\n", + "type": [ + "object", + "null" + ], "properties": { "caseId": { "description": "The case identifier for Swimlane connectors.", @@ -5895,8 +6638,10 @@ }, "destIp": { "description": "Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "impact": { "description": "The effect an incident had on business for ServiceNow ITSM connectors.", @@ -5915,13 +6660,17 @@ }, "malwareHash": { "description": "Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "malwareUrl": { "description": "Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "parent": { "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", @@ -5941,8 +6690,10 @@ }, "sourceIp": { "description": "Indicates whether cases will send a comma-separated list of source IPs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "subcategory": { "description": "The subcategory of the incident for ServiceNow ITSM connectors.", @@ -5953,17 +6704,23 @@ "type": "string" } }, - "example": null + "examples": [ + null + ] }, "id": { "description": "The identifier for the connector. To create a case without a connector, use `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. To create a case without a connector, use `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -5982,9 +6739,11 @@ "type": "object", "properties": { "fields": { - "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.", - "nullable": true, - "type": "object", + "description": "An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value.\n", + "type": [ + "object", + "null" + ], "properties": { "caseId": { "description": "The case identifier for Swimlane connectors.", @@ -5996,8 +6755,10 @@ }, "destIp": { "description": "Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "impact": { "description": "The effect an incident had on business for ServiceNow ITSM connectors.", @@ -6016,13 +6777,17 @@ }, "malwareHash": { "description": "Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "malwareUrl": { "description": "Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "parent": { "description": "The key of the parent issue, when the issue type is sub-task for Jira connectors.", @@ -6042,8 +6807,10 @@ }, "sourceIp": { "description": "Indicates whether cases will send a comma-separated list of source IPs for ServiceNow SecOps connectors.", - "type": "boolean", - "nullable": true + "type": [ + "boolean", + "null" + ] }, "subcategory": { "description": "The subcategory of the incident for ServiceNow ITSM connectors.", @@ -6054,17 +6821,23 @@ "type": "string" } }, - "example": null + "examples": [ + null + ] }, "id": { "description": "The identifier for the connector. To create a case without a connector, use `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "name": { "description": "The name of the connector. To create a case without a connector, use `none`.", "type": "string", - "example": "none" + "examples": [ + "none" + ] }, "type": { "$ref": "#/components/schemas/connector_types" @@ -6089,11 +6862,13 @@ "tags": { "type": "array", "items": { - "type": "string", - "example": [ + "type": "string" + }, + "examples": [ + [ "tag-1" ] - } + ] }, "title": { "type": "string" @@ -6101,9 +6876,11 @@ } }, "payload_delete": { - "type": "object", - "description": "If the `action` is `delete` and the `type` is `delete_case`, the payload is nullable.", - "nullable": true + "type": [ + "object", + "null" + ], + "description": "If the `action` is `delete` and the `type` is `delete_case`, the payload is nullable." }, "payload_description": { "type": "object", @@ -6153,8 +6930,10 @@ "items": { "type": "string" }, - "example": [ - "tag-1" + "examples": [ + [ + "tag-1" + ] ] } } @@ -6206,7 +6985,9 @@ "settings", "severity" ], - "example": "create_case" + "examples": [ + "create_case" + ] }, "user_actions_response_properties": { "type": "object", @@ -6227,43 +7008,67 @@ }, "action_id": { "type": "string", - "example": "22fd3e30-03b1-11ed-920c-974bfa104448" + "examples": [ + "22fd3e30-03b1-11ed-920c-974bfa104448" + ] }, "case_id": { "type": "string", - "example": "22df07d0-03b1-11ed-920c-974bfa104448" + "examples": [ + "22df07d0-03b1-11ed-920c-974bfa104448" + ] }, "comment_id": { - "type": "string", - "nullable": true, - "example": "578608d0-03b1-11ed-920c-974bfa104448" + "type": [ + "string", + "null" + ], + "examples": [ + "578608d0-03b1-11ed-920c-974bfa104448" + ] }, "created_at": { "type": "string", "format": "date-time", - "example": "2022-05-13T09:16:17.416Z" + "examples": [ + "2022-05-13T09:16:17.416Z" + ] }, "created_by": { "type": "object", "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -6341,36 +7146,56 @@ "$ref": "#/components/schemas/actions" }, "comment_id": { - "type": "string", - "nullable": true, - "example": "578608d0-03b1-11ed-920c-974bfa104448" + "type": [ + "string", + "null" + ], + "examples": [ + "578608d0-03b1-11ed-920c-974bfa104448" + ] }, "created_at": { "type": "string", "format": "date-time", - "example": "2022-05-13T09:16:17.416Z" + "examples": [ + "2022-05-13T09:16:17.416Z" + ] }, "created_by": { "type": "object", "properties": { "email": { - "type": "string", - "example": null, - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "full_name": { - "type": "string", - "example": null, - "nullable": true - }, - "username": { - "type": "string", - "example": "elastic", - "nullable": true + "type": [ + "string", + "null" + ], + "examples": [ + null + ] }, "profile_uid": { "type": "string", - "example": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + "examples": [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0" + ] + }, + "username": { + "type": [ + "string", + "null" + ], + "examples": [ + "elastic" + ] } }, "required": [ @@ -6381,7 +7206,9 @@ }, "id": { "type": "string", - "example": "22fd3e30-03b1-11ed-920c-974bfa104448" + "examples": [ + "22fd3e30-03b1-11ed-920c-974bfa104448" + ] }, "owner": { "$ref": "#/components/schemas/owners" @@ -6431,7 +7258,9 @@ }, "version": { "type": "string", - "example": "WzM1ODg4LDFd" + "examples": [ + "WzM1ODg4LDFd" + ] }, "type": { "type": "string", @@ -6449,7 +7278,9 @@ "settings", "severity" ], - "example": "create_case" + "examples": [ + "create_case" + ] } } } diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index 8347d7d85741b..e837ef766ab0c 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Cases description: OpenAPI schema for Cases endpoints - version: '0.1' + version: '0.2' contact: name: Cases Team license: @@ -194,7 +194,7 @@ paths: title: type: string description: The case title. - example: + examples: - id: 06116b80-e1c3-11ec-be9b-9b1838238ee6 title: security_case '401': @@ -258,22 +258,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -282,27 +286,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -310,46 +324,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: getConfigurationResponse: $ref: '#/components/examples/get_case_configuration_response' @@ -419,22 +447,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -443,27 +475,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -471,46 +513,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: setCaseConfigResponse: $ref: '#/components/examples/set_case_configuration_response' @@ -582,22 +638,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -606,27 +666,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -634,46 +704,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: updateCaseConfigurationResponse: $ref: '#/components/examples/update_case_configuration_response' @@ -709,20 +793,27 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic examples: getReportersResponse: $ref: '#/components/examples/get_reporters_response' @@ -1028,8 +1119,9 @@ paths: content: application/json: schema: - type: object - nullable: true + type: + - object + - 'null' responses: '200': description: Indicates a successful call. @@ -1344,7 +1436,7 @@ paths: title: type: string description: The case title. - example: + examples: - id: 06116b80-e1c3-11ec-be9b-9b1838238ee6 title: security_case '401': @@ -1409,22 +1501,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -1433,27 +1529,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -1461,46 +1567,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: getConfigurationResponse: $ref: '#/components/examples/get_case_configuration_response' @@ -1571,22 +1691,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -1595,27 +1719,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -1623,46 +1757,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: setCaseConfigResponse: $ref: '#/components/examples/set_case_configuration_response' @@ -1735,22 +1883,26 @@ paths: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' created_at: type: string format: date-time - example: '2022-06-01T17:07:17.767Z' + examples: + - '2022-06-01T17:07:17.767Z' created_by: type: object required: @@ -1759,27 +1911,37 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic error: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -1787,46 +1949,60 @@ paths: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: '#/components/schemas/owners' updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: '2022-06-01T19:58:48.169Z' + examples: + - '2022-06-01T19:58:48.169Z' updated_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzIwNzMsMV0= + examples: + - WzIwNzMsMV0= examples: updateCaseConfigurationResponse: $ref: '#/components/examples/update_case_configuration_response' @@ -1913,20 +2089,27 @@ paths: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic examples: getReportersResponse: $ref: '#/components/examples/get_reporters_response' @@ -2269,8 +2452,9 @@ paths: content: application/json: schema: - type: object - nullable: true + type: + - object + - 'null' responses: '200': description: Indicates a successful call. @@ -2326,7 +2510,6 @@ paths: - $ref: '#/components/parameters/case_id' - $ref: '#/components/parameters/space_id' - $ref: '#/components/parameters/page_index' - example: '1' - $ref: '#/components/parameters/page_size' - $ref: '#/components/parameters/sort_order' - $ref: '#/components/parameters/user_action_types' @@ -2365,7 +2548,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' parameters: kbn_xsrf: schema: @@ -2386,7 +2570,8 @@ components: type: string minItems: 1 maxItems: 100 - example: d4e7abb0-b462-11ec-9a8d-698504725a43 + examples: + - - d4e7abb0-b462-11ec-9a8d-698504725a43 assignees: in: query name: assignees @@ -2410,7 +2595,8 @@ components: items: type: string maxItems: 100 - example: my-category + examples: + - - my-category defaultSearchOperator: in: query name: defaultSearchOperator @@ -2418,7 +2604,8 @@ components: schema: type: string default: OR - example: OR + examples: + - OR from: in: query name: from @@ -2426,7 +2613,8 @@ components: Returns only cases that were created after a specific date. The date must be specified as a KQL data range or date match expression. schema: type: string - example: now-1d + examples: + - now-1d owner: in: query name: owner @@ -2438,7 +2626,8 @@ components: - type: array items: $ref: '#/components/schemas/owners' - example: cases + examples: + - - cases page_index: in: query name: page @@ -2447,6 +2636,8 @@ components: schema: type: integer default: 1 + examples: + - 1 page_size: in: query name: perPage @@ -2467,7 +2658,8 @@ components: items: type: string maxItems: 100 - example: elastic + examples: + - - elastic search: in: query name: search @@ -2510,7 +2702,8 @@ components: - status - severity default: createdAt - example: updatedAt + examples: + - updatedAt sort_order: in: query name: sortOrder @@ -2532,7 +2725,8 @@ components: - closed - in-progress - open - example: open + examples: + - open tags: in: query name: tags @@ -2544,7 +2738,8 @@ components: items: type: string maxItems: 100 - example: tag-1 + examples: + - - tag-1 to: in: query name: to @@ -2552,7 +2747,8 @@ components: Returns only cases that were created before a specific date. The date must be specified as a KQL data range or date match expression. schema: type: string - example: now+1d + examples: + - now+1d alert_id: in: path name: alertId @@ -2560,7 +2756,8 @@ components: required: true schema: type: string - example: 09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540 + examples: + - 09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540 configuration_id: in: path name: configurationId @@ -2568,7 +2765,8 @@ components: required: true schema: type: string - example: 3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9 + examples: + - 3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9 case_id: in: path name: caseId @@ -2576,7 +2774,8 @@ components: required: true schema: type: string - example: 9c235210-6834-11ea-a78c-6ffb38a34414 + examples: + - 9c235210-6834-11ea-a78c-6ffb38a34414 includeComments: in: query name: includeComments @@ -2593,7 +2792,8 @@ components: required: true schema: type: string - example: 71ec1870-725b-11ea-a0b2-c51ea50a58e2 + examples: + - 71ec1870-725b-11ea-a0b2-c51ea50a58e2 connector_id: in: path name: connectorId @@ -2601,7 +2801,8 @@ components: required: true schema: type: string - example: abed3a70-71bd-11ea-a0b2-c51ea50a58e2 + examples: + - abed3a70-71bd-11ea-a0b2-c51ea50a58e2 user_action_types: in: query name: types @@ -2626,7 +2827,8 @@ components: - tags - title - user - example: create_case + examples: + - - create_case space_id: in: path name: spaceId @@ -2634,13 +2836,15 @@ components: required: true schema: type: string - example: default + examples: + - default schemas: assignees: - type: array + type: + - array + - 'null' description: An array containing users that are assigned to the case. maxItems: 10 - nullable: true items: type: object required: @@ -2649,7 +2853,8 @@ components: uid: type: string description: A unique identifier for the user profile. These identifiers can be found by using the suggest user profile API. - example: u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0 + examples: + - u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0 connector_properties_none: title: Create or update case request properties for no connector required: @@ -2662,21 +2867,26 @@ components: properties: fields: description: An object containing the connector fields. To create a case without a connector, specify null. To update a case to remove the connector, specify null. - nullable: true - type: string - example: null + type: + - string + - 'null' + examples: + - null id: description: The identifier for the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`. type: string - example: none + examples: + - none name: description: The name of the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`. type: string - example: none + examples: + - none type: description: The type of connector. To create a case without a connector, use `.none`. To update a case to remove the connector, specify `.none`. type: string - example: .none + examples: + - .none enum: - .none connector_properties_cases_webhook: @@ -2690,9 +2900,11 @@ components: type: object properties: fields: - type: string - nullable: true - example: null + type: + - string + - 'null' + examples: + - null id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -2702,7 +2914,8 @@ components: type: description: The type of connector. type: string - example: .cases-webhook + examples: + - .cases-webhook enum: - .cases-webhook connector_properties_jira: @@ -2725,16 +2938,19 @@ components: properties: issueType: description: The type of issue. - type: string - nullable: true + type: + - string + - 'null' parent: description: The key of the parent issue, when the issue type is sub-task. - type: string - nullable: true + type: + - string + - 'null' priority: description: The priority of the issue. - type: string - nullable: true + type: + - string + - 'null' id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -2744,7 +2960,8 @@ components: type: description: The type of connector. type: string - example: .jira + examples: + - .jira enum: - .jira connector_properties_resilient: @@ -2759,8 +2976,9 @@ components: properties: fields: description: An object containing the connector fields. If you want to omit any individual field, specify null as its value. - type: object - nullable: true + type: + - object + - 'null' required: - issueTypes - severityCode @@ -2782,7 +3000,8 @@ components: type: description: The type of connector. type: string - example: .resilient + examples: + - .resilient enum: - .resilient connector_properties_servicenow: @@ -2807,24 +3026,29 @@ components: properties: category: description: The category of the incident. - type: string - nullable: true + type: + - string + - 'null' impact: description: The effect an incident had on business. - type: string - nullable: true + type: + - string + - 'null' severity: description: The severity of the incident. - type: string - nullable: true + type: + - string + - 'null' subcategory: description: The subcategory of the incident. - type: string - nullable: true + type: + - string + - 'null' urgency: description: The extent to which the incident resolution can be delayed. - type: string - nullable: true + type: + - string + - 'null' id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -2834,7 +3058,8 @@ components: type: description: The type of connector. type: string - example: .servicenow + examples: + - .servicenow enum: - .servicenow connector_properties_servicenow_sir: @@ -2861,32 +3086,39 @@ components: properties: category: description: The category of the incident. - type: string - nullable: true + type: + - string + - 'null' destIp: description: Indicates whether cases will send a comma-separated list of destination IPs. - type: boolean - nullable: true + type: + - boolean + - 'null' malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes. - type: boolean - nullable: true + type: + - boolean + - 'null' malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs. - type: boolean - nullable: true + type: + - boolean + - 'null' priority: description: The priority of the issue. - type: string - nullable: true + type: + - string + - 'null' sourceIp: description: Indicates whether cases will send a comma-separated list of source IPs. - type: boolean - nullable: true + type: + - boolean + - 'null' subcategory: description: The subcategory of the incident. - type: string - nullable: true + type: + - string + - 'null' id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -2896,7 +3128,8 @@ components: type: description: The type of connector. type: string - example: .servicenow-sir + examples: + - .servicenow-sir enum: - .servicenow-sir connector_properties_swimlane: @@ -2917,8 +3150,9 @@ components: properties: caseId: description: The case identifier for Swimlane connectors. - type: string - nullable: true + type: + - string + - 'null' id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -2928,7 +3162,8 @@ components: type: description: The type of connector. type: string - example: .swimlane + examples: + - .swimlane enum: - .swimlane owners: @@ -2939,7 +3174,8 @@ components: - cases - observability - securitySolution - example: cases + examples: + - cases settings: type: object description: An object that contains the case settings. @@ -2949,7 +3185,8 @@ components: syncAlerts: description: Turns alert syncing on or off. type: boolean - example: true + examples: + - true severity_property: type: string description: The severity of the case. @@ -3036,31 +3273,40 @@ components: description: | The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`. oneOf: - - type: string + - type: + - string + - 'null' minLength: 1 maxLength: 160 - nullable: true - type: boolean case_response_closed_by_properties: title: Case response properties for closed_by - type: object - nullable: true + type: + - object + - 'null' properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name @@ -3075,11 +3321,13 @@ components: type: array items: type: string - example: a6e12ac4-7bce-457b-84f6-d7ce8deb8446 + examples: + - a6e12ac4-7bce-457b-84f6-d7ce8deb8446 created_at: type: string format: date-time - example: '2023-11-06T19:29:38.424Z' + examples: + - '2023-11-06T19:29:38.424Z' created_by: type: object required: @@ -3088,171 +3336,226 @@ components: - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic id: type: string - example: 73362370-ab1a-11ec-985f-97e55adae8b9 + examples: + - 73362370-ab1a-11ec-985f-97e55adae8b9 index: type: array items: type: string - example: .internal.alerts-security.alerts-default-000001 + examples: + - .internal.alerts-security.alerts-default-000001 owner: $ref: '#/components/schemas/owners' pushed_at: - type: string + type: + - string + - 'null' format: date-time - example: null - nullable: true + examples: + - null pushed_by: - type: object + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic rule: type: object properties: id: description: The rule identifier. type: string - example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + examples: + - 94d80550-aaf4-11ec-985f-97e55adae8b9 name: description: The rule name. type: string - example: security_rule + examples: + - security_rule type: type: string - example: alert + examples: + - alert enum: - alert updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true updated_by: - type: object - nullable: true + type: + - object + - 'null' required: - email - full_name - username properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic version: type: string - example: WzMwNDgsMV0= + examples: + - WzMwNDgsMV0= case_response_created_by_properties: title: Case response properties for created_by type: object properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name - username case_response_pushed_by_properties: title: Case response properties for pushed_by - type: object - nullable: true + type: + - object + - 'null' properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name - username case_response_updated_by_properties: title: Case response properties for updated_by - type: object - nullable: true + type: + - object + - 'null' properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name @@ -3265,43 +3568,53 @@ components: properties: comment: type: string - example: A new comment. + examples: + - A new comment. created_at: type: string format: date-time - example: '2022-05-13T09:16:17.416Z' + examples: + - '2022-05-13T09:16:17.416Z' created_by: $ref: '#/components/schemas/case_response_created_by_properties' id: type: string - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 owner: $ref: '#/components/schemas/owners' pushed_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: null + examples: + - null pushed_by: $ref: '#/components/schemas/case_response_pushed_by_properties' type: type: string - example: user + examples: + - user enum: - user updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true - example: null + examples: + - null updated_by: $ref: '#/components/schemas/case_response_updated_by_properties' version: type: string - example: WzIwNDMxLDFd + examples: + - WzIwNDMxLDFd external_service: - type: object - nullable: true + type: + - object + - 'null' properties: connector_id: type: string @@ -3317,24 +3630,32 @@ components: type: string format: date-time pushed_by: - type: object + type: + - object + - 'null' properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 - nullable: true + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic status: type: string description: The status of the case. @@ -3371,13 +3692,15 @@ components: assignees: $ref: '#/components/schemas/assignees' category: - type: string + type: + - string + - 'null' description: The case category. - nullable: true closed_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true closed_by: $ref: '#/components/schemas/case_response_closed_by_properties' comments: @@ -3406,7 +3729,8 @@ components: created_at: type: string format: date-time - example: '2022-05-13T09:16:17.416Z' + examples: + - '2022-05-13T09:16:17.416Z' created_by: $ref: '#/components/schemas/case_response_created_by_properties' customFields: @@ -3431,25 +3755,30 @@ components: description: | The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`. oneOf: - - type: string + - type: + - string + - 'null' minLength: 1 maxLength: 160 - nullable: true - type: boolean description: type: string - example: A case description. + examples: + - A case description. duration: - type: integer + type: + - integer + - 'null' description: | The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero. - nullable: true - example: 120 + examples: + - 120 external_service: $ref: '#/components/schemas/external_service' id: type: string - example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + examples: + - 66b9aa00-94fa-11ea-9f74-e7e108796192 owner: $ref: '#/components/schemas/owners' settings: @@ -3462,38 +3791,45 @@ components: type: array items: type: string - example: - - tag-1 + examples: + - - tag-1 title: type: string - example: Case title 1 + examples: + - Case title 1 totalAlerts: type: integer - example: 0 + examples: + - 0 totalComment: type: integer - example: 0 + examples: + - 0 updated_at: - type: string + type: + - string + - 'null' format: date-time - nullable: true updated_by: $ref: '#/components/schemas/case_response_updated_by_properties' version: type: string - example: WzUzMiwxXQ== + examples: + - WzUzMiwxXQ== 4xx_response: type: object title: Unsuccessful cases API response properties: error: type: string - example: Unauthorized + examples: + - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 update_case_request: title: Update case request description: The update case API request body varies depending on the type of connector. @@ -3556,10 +3892,11 @@ components: description: | The custom field value. If the custom field is required, it cannot be explicitly set to null. However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`. oneOf: - - type: string + - type: + - string + - 'null' minLength: 1 maxLength: 160 - nullable: true - type: boolean description: description: An updated description for the case. @@ -3600,7 +3937,8 @@ components: enum: - close-by-pushing - close-by-user - example: close-by-user + examples: + - close-by-user connector_types: type: string description: The type of connector. @@ -3612,7 +3950,8 @@ components: - .servicenow - .servicenow-sir - .swimlane - example: .none + examples: + - .none set_case_configuration_request: title: Set case configuration request description: External connection details, such as the closure type and default connector for cases. @@ -3630,16 +3969,19 @@ components: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' required: @@ -3699,16 +4041,19 @@ components: properties: fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - object + - 'null' id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' required: @@ -3753,7 +4098,8 @@ components: description: | The version of the connector. To retrieve the version value, use the get configuration API. type: string - example: WzIwMiwxXQ== + examples: + - WzIwMiwxXQ== alert_response_properties: type: object properties: @@ -3777,7 +4123,8 @@ components: type: string maxItems: 1000 x-technical-preview: true - example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 + examples: + - 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 alert_indices: title: Alert indices description: | @@ -3799,11 +4146,13 @@ components: id: description: The rule identifier. type: string - example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + examples: + - 94d80550-aaf4-11ec-985f-97e55adae8b9 name: description: The rule name. type: string - example: security_rule + examples: + - security_rule add_alert_comment_request_properties: title: Add case comment request properties for alerts required: @@ -3826,7 +4175,8 @@ components: type: description: The type of comment. type: string - example: alert + examples: + - alert enum: - alert add_user_comment_request_properties: @@ -3838,13 +4188,15 @@ components: description: The new comment. It is required only when `type` is `user`. type: string maxLength: 30000 - example: A new comment. + examples: + - A new comment. owner: $ref: '#/components/schemas/owners' type: type: string description: The type of comment. - example: user + examples: + - user enum: - user required: @@ -3878,7 +4230,8 @@ components: type: string description: | The identifier for the comment. To retrieve comment IDs, use the get comments API. - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 index: $ref: '#/components/schemas/alert_indices' owner: @@ -3890,12 +4243,14 @@ components: type: string enum: - alert - example: alert + examples: + - alert version: description: | The current comment version. To retrieve version values, use the get comments API. type: string - example: Wzk1LDFd + examples: + - Wzk1LDFd update_user_comment_request_properties: title: Update case comment request properties for user comments description: Defines properties for case comment requests when type is user. @@ -3905,12 +4260,14 @@ components: description: The new comment. It is required only when `type` is `user`. type: string maxLength: 30000 - example: A new comment. + examples: + - A new comment. id: type: string description: | The identifier for the comment. To retrieve comment IDs, use the get comments API. - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 owner: $ref: '#/components/schemas/owners' type: @@ -3918,12 +4275,14 @@ components: description: The type of comment. enum: - user - example: user + examples: + - user version: description: | The current comment version. To retrieve version values, use the get comments API. type: string - example: Wzk1LDFd + examples: + - Wzk1LDFd required: - comment - id @@ -3946,7 +4305,8 @@ components: - delete - push_to_service - update - example: create + examples: + - create payload_alert_comment: type: object properties: @@ -3956,14 +4316,16 @@ components: alertId: oneOf: - type: string - example: 1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d + examples: + - 1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d - type: array items: type: string index: oneOf: - type: string - example: .alerts-observability.logs.alerts-default + examples: + - .alerts-observability.logs.alerts-default - type: array items: type: string @@ -3975,11 +4337,13 @@ components: id: description: The rule identifier. type: string - example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + examples: + - 94d80550-aaf4-11ec-985f-97e55adae8b9 name: description: The rule name. type: string - example: security_rule + examples: + - security_rule type: type: string enum: @@ -3996,9 +4360,11 @@ components: type: object properties: fields: - description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. - nullable: true - type: object + description: | + An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. + type: + - object + - 'null' properties: caseId: description: The case identifier for Swimlane connectors. @@ -4008,8 +4374,9 @@ components: type: string destIp: description: Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' impact: description: The effect an incident had on business for ServiceNow ITSM connectors. type: string @@ -4023,12 +4390,14 @@ components: type: string malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' parent: description: The key of the parent issue, when the issue type is sub-task for Jira connectors. type: string @@ -4043,23 +4412,27 @@ components: type: string sourceIp: description: Indicates whether cases will send a comma-separated list of source IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' subcategory: description: The subcategory of the incident for ServiceNow ITSM connectors. type: string urgency: description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. type: string - example: null + examples: + - null id: description: The identifier for the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none name: description: The name of the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' payload_create_case: @@ -4071,9 +4444,11 @@ components: type: object properties: fields: - description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. - nullable: true - type: object + description: | + An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. + type: + - object + - 'null' properties: caseId: description: The case identifier for Swimlane connectors. @@ -4083,8 +4458,9 @@ components: type: string destIp: description: Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' impact: description: The effect an incident had on business for ServiceNow ITSM connectors. type: string @@ -4098,12 +4474,14 @@ components: type: string malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' parent: description: The key of the parent issue, when the issue type is sub-task for Jira connectors. type: string @@ -4118,23 +4496,27 @@ components: type: string sourceIp: description: Indicates whether cases will send a comma-separated list of source IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - boolean + - 'null' subcategory: description: The subcategory of the incident for ServiceNow ITSM connectors. type: string urgency: description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. type: string - example: null + examples: + - null id: description: The identifier for the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none name: description: The name of the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none type: $ref: '#/components/schemas/connector_types' description: @@ -4151,14 +4533,15 @@ components: type: array items: type: string - example: - - tag-1 + examples: + - - tag-1 title: type: string payload_delete: - type: object + type: + - object + - 'null' description: If the `action` is `delete` and the `type` is `delete_case`, the payload is nullable. - nullable: true payload_description: type: object properties: @@ -4191,8 +4574,8 @@ components: type: array items: type: string - example: - - tag-1 + examples: + - - tag-1 payload_title: type: object properties: @@ -4228,7 +4611,8 @@ components: - status - settings - severity - example: create_case + examples: + - create_case user_actions_response_properties: type: object required: @@ -4246,36 +4630,48 @@ components: $ref: '#/components/schemas/actions' action_id: type: string - example: 22fd3e30-03b1-11ed-920c-974bfa104448 + examples: + - 22fd3e30-03b1-11ed-920c-974bfa104448 case_id: type: string - example: 22df07d0-03b1-11ed-920c-974bfa104448 + examples: + - 22df07d0-03b1-11ed-920c-974bfa104448 comment_id: - type: string - nullable: true - example: 578608d0-03b1-11ed-920c-974bfa104448 + type: + - string + - 'null' + examples: + - 578608d0-03b1-11ed-920c-974bfa104448 created_at: type: string format: date-time - example: '2022-05-13T09:16:17.416Z' + examples: + - '2022-05-13T09:16:17.416Z' created_by: type: object properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name @@ -4315,38 +4711,49 @@ components: action: $ref: '#/components/schemas/actions' comment_id: - type: string - nullable: true - example: 578608d0-03b1-11ed-920c-974bfa104448 + type: + - string + - 'null' + examples: + - 578608d0-03b1-11ed-920c-974bfa104448 created_at: type: string format: date-time - example: '2022-05-13T09:16:17.416Z' + examples: + - '2022-05-13T09:16:17.416Z' created_by: type: object properties: email: - type: string - example: null - nullable: true + type: + - string + - 'null' + examples: + - null full_name: - type: string - example: null - nullable: true - username: - type: string - example: elastic - nullable: true + type: + - string + - 'null' + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + username: + type: + - string + - 'null' + examples: + - elastic required: - email - full_name - username id: type: string - example: 22fd3e30-03b1-11ed-920c-974bfa104448 + examples: + - 22fd3e30-03b1-11ed-920c-974bfa104448 owner: $ref: '#/components/schemas/owners' payload: @@ -4366,7 +4773,8 @@ components: - $ref: '#/components/schemas/payload_user_comment' version: type: string - example: WzM1ODg4LDFd + examples: + - WzM1ODg4LDFd type: type: string description: The type of action. @@ -4382,7 +4790,8 @@ components: - status - settings - severity - example: create_case + examples: + - create_case examples: create_case_request: summary: Create a security case that uses a Jira connector. diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/alert_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/alert_id.yaml index 8677b327b91be..24c728f017d12 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/alert_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/alert_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the alert. required: true schema: type: string - example: 09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540 \ No newline at end of file + examples: + - 09f0c261e39e36351d75995b78bb83673774d1bc2cca9df2d15f0e5c0a99a540 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml index eebde85823746..de7cfebbeb6bf 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/case_id.yaml @@ -4,4 +4,5 @@ description: The identifier for the case. To retrieve case IDs, use the find cas required: true schema: type: string - example: 9c235210-6834-11ea-a78c-6ffb38a34414 \ No newline at end of file + examples: + - 9c235210-6834-11ea-a78c-6ffb38a34414 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/category.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/category.yaml index 8bf20d9aa2450..8d28898750ae2 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/category.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/category.yaml @@ -8,4 +8,5 @@ schema: items: type: string maxItems: 100 -example: my-category \ No newline at end of file + examples: + - [ my-category ] \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/comment_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/comment_id.yaml index a46f47569e8d2..852ad328c6c4e 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/comment_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/comment_id.yaml @@ -6,4 +6,5 @@ description: > required: true schema: type: string - example: '71ec1870-725b-11ea-a0b2-c51ea50a58e2' \ No newline at end of file + examples: + - '71ec1870-725b-11ea-a0b2-c51ea50a58e2' \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/configuration_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/configuration_id.yaml index 65cce12afaa92..884821a79952f 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/configuration_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/configuration_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the configuration. required: true schema: type: string - example: 3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9 \ No newline at end of file + examples: + - 3297a0f0-b5ec-11ec-b141-0fdb20a7f9a9 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/connector_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/connector_id.yaml index 71cdc7191cfa1..7fc146b22126c 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/connector_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/connector_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the connector. To retrieve connector IDs, use the required: true schema: type: string - example: abed3a70-71bd-11ea-a0b2-c51ea50a58e2 \ No newline at end of file + examples: + - abed3a70-71bd-11ea-a0b2-c51ea50a58e2 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/defaultSearchOperator.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/defaultSearchOperator.yaml index 8e9004c859b46..cd3cf8cb1c005 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/defaultSearchOperator.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/defaultSearchOperator.yaml @@ -4,4 +4,5 @@ description: he default operator to use for the simple_query_string. schema: type: string default: OR -example: OR \ No newline at end of file + examples: + - OR \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml index 6f9a24dae5956..bf92b68ad6ba3 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/from.yaml @@ -5,4 +5,5 @@ description: > The date must be specified as a KQL data range or date match expression. schema: type: string - example: now-1d \ No newline at end of file + examples: + - now-1d \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml index c84ec64ab2a53..acd48cd0955a2 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml @@ -10,4 +10,5 @@ schema: type: string minItems: 1 maxItems: 100 -example: d4e7abb0-b462-11ec-9a8d-698504725a43 + examples: + - [ d4e7abb0-b462-11ec-9a8d-698504725a43 ] diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/owner.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/owner.yaml index 3c5e511742bf2..d4f40a4403723 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/owner.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/owner.yaml @@ -10,4 +10,5 @@ schema: - type: array items: $ref: '../schemas/owners.yaml' -example: cases \ No newline at end of file + examples: + - [ cases ] \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/page_index.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/page_index.yaml index 9176d3b62094e..8ac69a105d15b 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/page_index.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/page_index.yaml @@ -5,3 +5,5 @@ required: false schema: type: integer default: 1 + examples: + - 1 diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/reporters.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/reporters.yaml index db28a6c48ae02..3d4d24cafd3ca 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/reporters.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/reporters.yaml @@ -8,4 +8,5 @@ schema: items: type: string maxItems: 100 -example: elastic + examples: + - [ elastic ] diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/sortField.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/sortField.yaml index 9df834cf9f5ac..d5a49214e9d90 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/sortField.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/sortField.yaml @@ -12,4 +12,5 @@ schema: - status - severity default: createdAt -example: updatedAt \ No newline at end of file + examples: + - updatedAt \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml index 0a9fba457e3e7..45787e844caec 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/space_id.yaml @@ -4,4 +4,5 @@ description: An identifier for the space. If `/s/` and the identifier are omitte required: true schema: type: string - example: default + examples: + - default diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/status.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/status.yaml index 0517e7516a87f..b90edcd58286b 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/status.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/status.yaml @@ -7,4 +7,5 @@ schema: - closed - in-progress - open -example: open \ No newline at end of file + examples: + - open \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/tags.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/tags.yaml index d899edbcc38eb..b1732fb124577 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/tags.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/tags.yaml @@ -8,4 +8,5 @@ schema: items: type: string maxItems: 100 -example: tag-1 + examples: + - [ tag-1 ] diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml index c176ce8407803..dd326cabd8dca 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/to.yaml @@ -5,4 +5,5 @@ description: > The date must be specified as a KQL data range or date match expression. schema: type: string -example: now+1d \ No newline at end of file + examples: + - now+1d \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/user_action_types.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/user_action_types.yaml index 2b04b7c806620..320dc67b631ca 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/user_action_types.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/user_action_types.yaml @@ -21,4 +21,5 @@ schema: - tags - title - user -example: create_case \ No newline at end of file + examples: + - [ create_case ] \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/4xx_response.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/4xx_response.yaml index 75d0ac39903bf..72d3bd82cbf60 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/4xx_response.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/4xx_response.yaml @@ -3,9 +3,11 @@ title: Unsuccessful cases API response properties: error: type: string - example: Unauthorized + examples: + - Unauthorized message: type: string statusCode: type: integer - example: 401 \ No newline at end of file + examples: + - 401 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/action_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/action_types.yaml index 3568008b07000..140b606b44565 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/action_types.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/action_types.yaml @@ -13,4 +13,5 @@ enum: - status - settings - severity -example: create_case \ No newline at end of file +examples: + - create_case \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/actions.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/actions.yaml index 1638ed67c78e2..f2b20517efd54 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/actions.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/actions.yaml @@ -5,4 +5,5 @@ enum: - delete - push_to_service - update -example: create \ No newline at end of file +examples: + - create \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml index c99ebb19cc818..192e12f62857c 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/add_alert_comment_request_properties.yaml @@ -19,6 +19,7 @@ properties: type: description: The type of comment. type: string - example: alert + examples: + - alert enum: - alert \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml index beac63c377ade..a0740dbdc51bc 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/add_user_comment_request_properties.yaml @@ -6,13 +6,15 @@ properties: description: The new comment. It is required only when `type` is `user`. type: string maxLength: 30000 - example: A new comment. + examples: + - A new comment. owner: $ref: 'owners.yaml' type: type: string description: The type of comment. - example: user + examples: + - user enum: - user required: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml index 443d9dcc55523..3305732cee6ec 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_comment_response_properties.yaml @@ -7,11 +7,13 @@ properties: type: array items: type: string - example: a6e12ac4-7bce-457b-84f6-d7ce8deb8446 + examples: + - a6e12ac4-7bce-457b-84f6-d7ce8deb8446 created_at: type: string format: date-time - example: 2023-11-06T19:29:38.424Z + examples: + - 2023-11-06T19:29:38.424Z created_by: type: object required: @@ -22,44 +24,52 @@ properties: $ref: 'user_properties.yaml' id: type: string - example: 73362370-ab1a-11ec-985f-97e55adae8b9 + examples: + - 73362370-ab1a-11ec-985f-97e55adae8b9 index: type: array items: type: string - example: .internal.alerts-security.alerts-default-000001 + examples: + - .internal.alerts-security.alerts-default-000001 owner: $ref: 'owners.yaml' pushed_at: - type: string + type: + - "string" + - "null" format: date-time - example: null - nullable: true + examples: + - null pushed_by: - type: object + type: + - "object" + - "null" required: - email - full_name - username properties: $ref: 'user_properties.yaml' - nullable: true rule: type: object properties: $ref: 'rule_properties.yaml' type: type: string - example: alert + examples: + - alert enum: - alert updated_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true updated_by: - type: object - nullable: true + type: + - "object" + - "null" required: - email - full_name @@ -68,4 +78,5 @@ properties: $ref: 'user_properties.yaml' version: type: string - example: WzMwNDgsMV0= \ No newline at end of file + examples: + - WzMwNDgsMV0= \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml index cca8eb74f5019..5a8f821931f57 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/alert_identifiers.yaml @@ -13,4 +13,5 @@ oneOf: type: string maxItems: 1000 x-technical-preview: true -example: 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 \ No newline at end of file +examples: + - 6b24c4dc44bc720cfc92797f3d61fff952f2b2627db1fb4f8cc49f4530c4ff42 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/assignees.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/assignees.yaml index 5b4e18517bd43..4109c4d476909 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/assignees.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/assignees.yaml @@ -1,7 +1,8 @@ -type: array +type: + - "array" + - "null" description: An array containing users that are assigned to the case. maxItems: 10 -nullable: true items: type: object required: @@ -10,4 +11,5 @@ items: uid: type: string description: A unique identifier for the user profile. These identifiers can be found by using the suggest user profile API. - example: u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0 \ No newline at end of file + examples: + - u_0wpfV1MqYDaXzLtRVY-gLMrddKDEmfz51Fszhj7hWC8_0 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_connector_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_connector_properties.yaml index ce1669c37941e..3873a8ae9e0f6 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_connector_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_connector_properties.yaml @@ -1,14 +1,17 @@ fields: description: The fields specified in the case configuration are not used and are not propagated to individual cases, therefore it is recommended to set it to `null`. - nullable: true - type: object + type: + - "object" + - "null" id: description: The identifier for the connector. If you do not want a default connector, use `none`. To retrieve connector IDs, use the find connectors API. type: string - example: none + examples: + - none name: description: The name of the connector. If you do not want a default connector, use `none`. To retrieve connector names, use the find connectors API. type: string - example: none + examples: + - none type: $ref: 'connector_types.yaml' \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml index 62bddb7a2597b..e85179f3053ac 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml @@ -15,7 +15,8 @@ connector: created_at: type: string format: date-time - example: 2022-06-01T17:07:17.767Z + examples: + - 2022-06-01T17:07:17.767Z created_by: type: object required: @@ -25,12 +26,15 @@ created_by: properties: $ref: 'user_properties.yaml' error: - type: string - nullable: true - example: null + type: + - "string" + - "null" + examples: + - null id: type: string - example: 4a97a440-e1cd-11ec-be9b-9b1838238ee6 + examples: + - 4a97a440-e1cd-11ec-be9b-9b1838238ee6 mappings: type: array items: @@ -38,29 +42,36 @@ mappings: properties: action_type: type: string - example: overwrite + examples: + - overwrite source: type: string - example: title + examples: + - title target: type: string - example: summary + examples: + - summary owner: $ref: 'owners.yaml' updated_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true - example: 2022-06-01T19:58:48.169Z + examples: + - 2022-06-01T19:58:48.169Z updated_by: - type: object + type: + - "object" + - "null" required: - email - full_name - username properties: $ref: 'user_properties.yaml' - nullable: true version: type: string - example: WzIwNzMsMV0= \ No newline at end of file + examples: + - WzIwNzMsMV0= \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_customfields.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_customfields.yaml index 4170833e818cc..5a4c9f26e09b2 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_customfields.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_customfields.yaml @@ -18,8 +18,9 @@ value: However, for cases that existed when the required custom field was added, the default value stored in Elasticsearch is `undefined`. The value returned in the API and user interface in this case is `null`. oneOf: - - type: string + - type: + - "string" + - "null" minLength: 1 maxLength: 160 - nullable: true - type: boolean diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml index 95bd14e4957a3..26b3eaa7395eb 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_closed_by_properties.yaml @@ -1,6 +1,7 @@ title: Case response properties for closed_by -type: object -nullable: true +type: + - "object" + - "null" properties: $ref: 'user_properties.yaml' required: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml index 1ac30d325d45e..18f79997e31ce 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_connector_field_properties.yaml @@ -1,7 +1,8 @@ title: Case response properties for connector fields -type: object +type: + - "object" + - "null" description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. -nullable: true properties: caseId: description: The case identifier for Swimlane connectors. @@ -11,8 +12,9 @@ properties: type: string destIp: description: Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" impact: description: The effect an incident had on business for ServiceNow ITSM connectors. type: string @@ -26,12 +28,14 @@ properties: type: string malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" parent: description: The key of the parent issue, when the issue type is sub-task for Jira connectors. type: string diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml index 60c6520d2f4c3..8f3de83d88c69 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml @@ -26,13 +26,15 @@ properties: assignees: $ref: 'assignees.yaml' category: - type: string + type: + - "string" + - "null" description: The case category. - nullable: true closed_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true closed_by: $ref: 'case_response_closed_by_properties.yaml' comments: @@ -61,7 +63,8 @@ properties: created_at: type: string format: date-time - example: '2022-05-13T09:16:17.416Z' + examples: + - '2022-05-13T09:16:17.416Z' created_by: $ref: 'case_response_created_by_properties.yaml' customFields: @@ -74,21 +77,25 @@ properties: $ref: 'case_customfields.yaml' description: type: string - example: A case description. + examples: + - A case description. duration: - type: integer + type: + - "integer" + - "null" description: > The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case was closed after less than half a second, the duration is rounded down to zero. - nullable: true - example: 120 + examples: + - 120 external_service: $ref: 'external_service.yaml' id: type: string - example: 66b9aa00-94fa-11ea-9f74-e7e108796192 + examples: + - 66b9aa00-94fa-11ea-9f74-e7e108796192 owner: $ref: 'owners.yaml' settings: @@ -101,23 +108,28 @@ properties: type: array items: type: string - example: - - tag-1 + examples: + - [tag-1] title: type: string - example: Case title 1 + examples: + - Case title 1 totalAlerts: type: integer - example: 0 + examples: + - 0 totalComment: type: integer - example: 0 + examples: + - 0 updated_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true updated_by: $ref: 'case_response_updated_by_properties.yaml' version: type: string - example: WzUzMiwxXQ== + examples: + - WzUzMiwxXQ== diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml index c59a5565c98b9..72f2c3ef619a2 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_pushed_by_properties.yaml @@ -1,6 +1,7 @@ title: Case response properties for pushed_by -type: object -nullable: true +type: + - "object" + - "null" properties: $ref: 'user_properties.yaml' required: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml index cd1bae033f2ff..8e475c5d205d6 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_updated_by_properties.yaml @@ -1,6 +1,7 @@ title: Case response properties for updated_by -type: object -nullable: true +type: + - "object" + - "null" properties: $ref: 'user_properties.yaml' required: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml index 6879f820d6f5c..8484d4d051ca4 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/closure_types.yaml @@ -3,4 +3,5 @@ description: Indicates whether a case is automatically closed when it is pushed enum: - close-by-pushing - close-by-user -example: close-by-user \ No newline at end of file +examples: + - close-by-user \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml index 9731b8ce4fad5..6a41e07aada44 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/comment_types.yaml @@ -3,4 +3,5 @@ description: The type of comment. enum: - alert - user -example: user \ No newline at end of file +examples: + - user \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml index 9416a31f38775..e68c226ee9b92 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties.yaml @@ -1,7 +1,11 @@ fields: - description: An object containing the connector fields. To create a case without a connector, specify null. If you want to omit any individual field, specify null as its value. - nullable: true - type: object + description: > + An object containing the connector fields. + To create a case without a connector, specify null. + If you want to omit any individual field, specify null as its value. + type: + - "object" + - "null" properties: caseId: description: The case identifier for Swimlane connectors. @@ -11,8 +15,9 @@ fields: type: string destIp: description: Indicates whether cases will send a comma-separated list of destination IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" impact: description: The effect an incident had on business for ServiceNow ITSM connectors. type: string @@ -26,12 +31,14 @@ fields: type: string malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" parent: description: The key of the parent issue, when the issue type is sub-task for Jira connectors. type: string @@ -46,22 +53,26 @@ fields: type: string sourceIp: description: Indicates whether cases will send a comma-separated list of source IPs for ServiceNow SecOps connectors. - type: boolean - nullable: true + type: + - "boolean" + - "null" subcategory: description: The subcategory of the incident for ServiceNow ITSM connectors. type: string urgency: description: The extent to which the incident resolution can be delayed for ServiceNow ITSM connectors. type: string - example: null + examples: + - null id: description: The identifier for the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none name: description: The name of the connector. To create a case without a connector, use `none`. type: string - example: none + examples: + - none type: $ref: 'connector_types.yaml' \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_cases_webhook.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_cases_webhook.yaml index 871b3180bc22e..b204dcbdd9f4d 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_cases_webhook.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_cases_webhook.yaml @@ -8,9 +8,11 @@ description: Defines properties for connectors when type is `.cases-webhook`. type: object properties: fields: - type: string - nullable: true - example: null + type: + - "string" + - "null" + examples: + - null id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -20,6 +22,7 @@ properties: type: description: The type of connector. type: string - example: .cases-webhook + examples: + - .cases-webhook enum: - .cases-webhook \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_jira.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_jira.yaml index a63f1fbd568d0..6eb1c0baa8e4e 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_jira.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_jira.yaml @@ -17,16 +17,19 @@ properties: properties: issueType: description: The type of issue. - type: string - nullable: true + type: + - "string" + - "null" parent: description: The key of the parent issue, when the issue type is sub-task. - type: string - nullable: true + type: + - "string" + - "null" priority: description: The priority of the issue. - type: string - nullable: true + type: + - "string" + - "null" id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -36,6 +39,7 @@ properties: type: description: The type of connector. type: string - example: .jira + examples: + - .jira enum: - .jira diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_none.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_none.yaml index c1bc49372f645..2497b1357c86c 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_none.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_none.yaml @@ -9,20 +9,25 @@ type: object properties: fields: description: An object containing the connector fields. To create a case without a connector, specify null. To update a case to remove the connector, specify null. - nullable: true - type: string - example: null + type: + - "string" + - "null" + examples: + - null id: description: The identifier for the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`. type: string - example: none + examples: + - none name: description: The name of the connector. To create a case without a connector, use `none`. To update a case to remove the connector, specify `none`. type: string - example: none + examples: + - none type: description: The type of connector. To create a case without a connector, use `.none`. To update a case to remove the connector, specify `.none`. type: string - example: .none + examples: + - .none enum: - .none \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_resilient.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_resilient.yaml index bf7929b3060e3..df8e34057de0e 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_resilient.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_resilient.yaml @@ -9,8 +9,9 @@ type: object properties: fields: description: An object containing the connector fields. If you want to omit any individual field, specify null as its value. - type: object - nullable: true + type: + - "object" + - "null" required: - issueTypes - severityCode @@ -32,6 +33,7 @@ properties: type: description: The type of connector. type: string - example: .resilient + examples: + - .resilient enum: - .resilient \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow.yaml index 5bc76ab7a9dd1..7a57a9e69ccc2 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow.yaml @@ -19,24 +19,29 @@ properties: properties: category: description: The category of the incident. - type: string - nullable: true + type: + - "string" + - "null" impact: description: The effect an incident had on business. - type: string - nullable: true + type: + - "string" + - "null" severity: description: The severity of the incident. - type: string - nullable: true + type: + - "string" + - "null" subcategory: description: The subcategory of the incident. - type: string - nullable: true + type: + - "string" + - "null" urgency: description: The extent to which the incident resolution can be delayed. - type: string - nullable: true + type: + - "string" + - "null" id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -46,6 +51,7 @@ properties: type: description: The type of connector. type: string - example: .servicenow + examples: + - .servicenow enum: - .servicenow \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow_sir.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow_sir.yaml index 42245f9771e39..cafff746d18f5 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow_sir.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_servicenow_sir.yaml @@ -21,32 +21,39 @@ properties: properties: category: description: The category of the incident. - type: string - nullable: true + type: + - "string" + - "null" destIp: description: Indicates whether cases will send a comma-separated list of destination IPs. - type: boolean - nullable: true + type: + - "boolean" + - "null" malwareHash: description: Indicates whether cases will send a comma-separated list of malware hashes. - type: boolean - nullable: true + type: + - "boolean" + - "null" malwareUrl: description: Indicates whether cases will send a comma-separated list of malware URLs. - type: boolean - nullable: true + type: + - "boolean" + - "null" priority: description: The priority of the issue. - type: string - nullable: true + type: + - "string" + - "null" sourceIp: description: Indicates whether cases will send a comma-separated list of source IPs. - type: boolean - nullable: true + type: + - "boolean" + - "null" subcategory: description: The subcategory of the incident. - type: string - nullable: true + type: + - "string" + - "null" id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -56,6 +63,7 @@ properties: type: description: The type of connector. type: string - example: .servicenow-sir + examples: + - .servicenow-sir enum: - .servicenow-sir \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_swimlane.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_swimlane.yaml index f4c138463078b..9cde6dd09d7c8 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_swimlane.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_properties_swimlane.yaml @@ -15,8 +15,9 @@ properties: properties: caseId: description: The case identifier for Swimlane connectors. - type: string - nullable: true + type: + - "string" + - "null" id: description: The identifier for the connector. To retrieve connector IDs, use the find connectors API. type: string @@ -26,6 +27,7 @@ properties: type: description: The type of connector. type: string - example: .swimlane + examples: + - .swimlane enum: - .swimlane \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml index fc23b9cab5f8a..4bc12b3ae9481 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/connector_types.yaml @@ -8,4 +8,5 @@ enum: - .servicenow - .servicenow-sir - .swimlane -example: .none \ No newline at end of file +examples: + - .none \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml index b3b3182b8c964..411f977928a8c 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/external_service.yaml @@ -1,5 +1,6 @@ -type: object -nullable: true +type: + - "object" + - "null" properties: connector_id: type: string @@ -15,7 +16,8 @@ properties: type: string format: date-time pushed_by: - type: object + type: + - "object" + - "null" properties: - $ref: 'user_properties.yaml' - nullable: true \ No newline at end of file + $ref: 'user_properties.yaml' \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml index 9036fd5a3833a..fa265756d9506 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/owners.yaml @@ -6,4 +6,5 @@ enum: - cases - observability - securitySolution -example: cases \ No newline at end of file +examples: + - cases \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_alert_comment.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_alert_comment.yaml index 0b0d3fc3c07ce..eaaaa539a33ca 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_alert_comment.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_alert_comment.yaml @@ -6,14 +6,16 @@ properties: alertId: oneOf: - type: string - example: 1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d + examples: + - 1c0b056b-cc9f-4b61-b5c9-cb801abd5e1d - type: array items: type: string index: oneOf: - type: string - example: .alerts-observability.logs.alerts-default + examples: + - .alerts-observability.logs.alerts-default - type: array items: type: string diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_create_case.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_create_case.yaml index 4c3043a25c7b8..9bda777bccead 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_create_case.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_create_case.yaml @@ -20,6 +20,7 @@ properties: type: array items: type: string - example: ["tag-1"] + examples: + - ["tag-1"] title: type: string \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_delete.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_delete.yaml index 933d91305dca7..29e16039ec273 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_delete.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_delete.yaml @@ -1,3 +1,4 @@ -type: object -description: If the `action` is `delete` and the `type` is `delete_case`, the payload is nullable. -nullable: true \ No newline at end of file +type: + - "object" + - "null" +description: If the `action` is `delete` and the `type` is `delete_case`, the payload is nullable. \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_tags.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_tags.yaml index bed767719e6f3..8ea6902802514 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/payload_tags.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/payload_tags.yaml @@ -4,4 +4,5 @@ properties: type: array items: type: string - example: ["tag-1"] \ No newline at end of file + examples: + - ["tag-1"] \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml index 09712b57ce138..0a4dfb828dcb5 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/rule.yaml @@ -10,8 +10,10 @@ properties: id: description: The rule identifier. type: string - example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + examples: + - 94d80550-aaf4-11ec-985f-97e55adae8b9 name: description: The rule name. type: string - example: security_rule \ No newline at end of file + examples: + - security_rule \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/rule_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/rule_properties.yaml index 64b93b77429a4..2ed5e0e89e8d7 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/rule_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/rule_properties.yaml @@ -1,8 +1,10 @@ id: description: The rule identifier. type: string - example: 94d80550-aaf4-11ec-985f-97e55adae8b9 + examples: + - 94d80550-aaf4-11ec-985f-97e55adae8b9 name: description: The rule name. type: string - example: security_rule \ No newline at end of file + examples: + - security_rule \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/settings.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/settings.yaml index a344eb0491951..576b8b9dff157 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/settings.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/settings.yaml @@ -6,4 +6,5 @@ properties: syncAlerts: description: Turns alert syncing on or off. type: boolean - example: true \ No newline at end of file + examples: + - true \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml index 2c7bd5dcc1215..5c5619cec298a 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_alert_comment_request_properties.yaml @@ -17,7 +17,8 @@ properties: description: > The identifier for the comment. To retrieve comment IDs, use the get comments API. - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 index: $ref: 'alert_indices.yaml' owner: @@ -29,10 +30,12 @@ properties: type: string enum: - alert - example: alert + examples: + - alert version: description: > The current comment version. To retrieve version values, use the get comments API. type: string - example: Wzk1LDFd \ No newline at end of file + examples: + - Wzk1LDFd \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml index 86ba2794ea19d..8a6a3aa7f302b 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml @@ -34,4 +34,5 @@ properties: The version of the connector. To retrieve the version value, use the get configuration API. type: string - example: WzIwMiwxXQ== \ No newline at end of file + examples: + - WzIwMiwxXQ== \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml index a83050e93eaaf..003f12d63a30d 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_user_comment_request_properties.yaml @@ -6,13 +6,15 @@ properties: description: The new comment. It is required only when `type` is `user`. type: string maxLength: 30000 - example: A new comment. + examples: + - A new comment. id: type: string description: > The identifier for the comment. To retrieve comment IDs, use the get comments API. - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 owner: $ref: 'owners.yaml' type: @@ -20,13 +22,15 @@ properties: description: The type of comment. enum: - user - example: user + examples: + - user version: description: > The current comment version. To retrieve version values, use the get comments API. type: string - example: Wzk1LDFd + examples: + - Wzk1LDFd required: - comment - id diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_find_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_find_response_properties.yaml index a17f98d8007ad..12b45d987598b 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_find_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_find_response_properties.yaml @@ -13,13 +13,16 @@ properties: action: $ref: 'actions.yaml' comment_id: - type: string - nullable: true - example: 578608d0-03b1-11ed-920c-974bfa104448 + type: + - "string" + - "null" + examples: + - 578608d0-03b1-11ed-920c-974bfa104448 created_at: type: string format: date-time - example: 2022-05-13T09:16:17.416Z + examples: + - 2022-05-13T09:16:17.416Z created_by: type: object properties: @@ -30,7 +33,8 @@ properties: - username id: type: string - example: 22fd3e30-03b1-11ed-920c-974bfa104448 + examples: + - 22fd3e30-03b1-11ed-920c-974bfa104448 owner: $ref: 'owners.yaml' payload: @@ -50,7 +54,8 @@ properties: - $ref: 'payload_user_comment.yaml' version: type: string - example: WzM1ODg4LDFd + examples: + - WzM1ODg4LDFd type: type: string description: The type of action. @@ -66,4 +71,5 @@ properties: - status - settings - severity - example: create_case + examples: + - create_case diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_response_properties.yaml index ef39c531c357d..02521d975d8de 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_actions_response_properties.yaml @@ -14,18 +14,23 @@ properties: $ref: 'actions.yaml' action_id: type: string - example: 22fd3e30-03b1-11ed-920c-974bfa104448 + examples: + - 22fd3e30-03b1-11ed-920c-974bfa104448 case_id: type: string - example: 22df07d0-03b1-11ed-920c-974bfa104448 + examples: + - 22df07d0-03b1-11ed-920c-974bfa104448 comment_id: - type: string - nullable: true - example: 578608d0-03b1-11ed-920c-974bfa104448 + type: + - "string" + - "null" + examples: + - 578608d0-03b1-11ed-920c-974bfa104448 created_at: type: string format: date-time - example: 2022-05-13T09:16:17.416Z + examples: + - 2022-05-13T09:16:17.416Z created_by: type: object properties: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml index b1727d3279abe..832d603e366dc 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_comment_response_properties.yaml @@ -5,37 +5,46 @@ required: properties: comment: type: string - example: A new comment. + examples: + - A new comment. created_at: type: string format: date-time - example: 2022-05-13T09:16:17.416Z + examples: + - 2022-05-13T09:16:17.416Z created_by: $ref: 'case_response_created_by_properties.yaml' id: type: string - example: 8af6ac20-74f6-11ea-b83a-553aecdb28b6 + examples: + - 8af6ac20-74f6-11ea-b83a-553aecdb28b6 owner: $ref: 'owners.yaml' pushed_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true - example: null + examples: + - null pushed_by: $ref: 'case_response_pushed_by_properties.yaml' type: type: string - example: user + examples: + - user enum: - user updated_at: - type: string + type: + - "string" + - "null" format: date-time - nullable: true - example: null + examples: + - null updated_by: $ref: 'case_response_updated_by_properties.yaml' version: type: string - example: WzIwNDMxLDFd \ No newline at end of file + examples: + - WzIwNDMxLDFd \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml index 19b76a6000c02..3c5439ac7aeee 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/user_properties.yaml @@ -1,15 +1,22 @@ email: - type: string - example: null - nullable: true + type: + - "string" + - "null" + examples: + - null full_name: - type: string - example: null - nullable: true -username: - type: string - example: elastic - nullable: true + type: + - "string" + - "null" + examples: + - null profile_uid: type: string - example: u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 + examples: + - u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0 +username: + type: + - "string" + - "null" + examples: + - elastic diff --git a/x-pack/plugins/cases/docs/openapi/entrypoint.yaml b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml index 840d49eaa852b..dff3dff43622c 100644 --- a/x-pack/plugins/cases/docs/openapi/entrypoint.yaml +++ b/x-pack/plugins/cases/docs/openapi/entrypoint.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Cases description: OpenAPI schema for Cases endpoints - version: '0.1' + version: '0.2' contact: name: Cases Team license: @@ -90,7 +90,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/paths/api@cases@alerts@{alertid}.yaml b/x-pack/plugins/cases/docs/openapi/paths/api@cases@alerts@{alertid}.yaml index 64ff49d17b8cd..7914c8a994c60 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/api@cases@alerts@{alertid}.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/api@cases@alerts@{alertid}.yaml @@ -28,7 +28,7 @@ get: title: type: string description: The case title. - example: + examples: - id: 06116b80-e1c3-11ec-be9b-9b1838238ee6 title: security_case '401': diff --git a/x-pack/plugins/cases/docs/openapi/paths/api@cases@{caseid}@connector@{connectorid}@_push.yaml b/x-pack/plugins/cases/docs/openapi/paths/api@cases@{caseid}@connector@{connectorid}@_push.yaml index 321f2f6938c50..8b52cf5d013cd 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/api@cases@{caseid}@connector@{connectorid}@_push.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/api@cases@{caseid}@connector@{connectorid}@_push.yaml @@ -14,8 +14,9 @@ post: content: application/json: schema: - type: object - nullable: true + type: + - "object" + - "null" responses: '200': description: Indicates a successful call. diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml index f1e287e05d304..0ffe229fd9e69 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@alerts@{alertid}.yaml @@ -28,7 +28,7 @@ get: title: type: string description: The case title. - example: + examples: - id: 06116b80-e1c3-11ec-be9b-9b1838238ee6 title: security_case '401': diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml index bee91522dde29..c06972c8dd0e8 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@connector@{connectorid}@_push.yaml @@ -18,8 +18,9 @@ post: content: application/json: schema: - type: object - nullable: true + type: + - "object" + - "null" responses: '200': description: Indicates a successful call. diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml index 0b5a6e660da8b..9fda51fa59792 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases@{caseid}@user_actions@_find.yaml @@ -11,7 +11,6 @@ get: - $ref: '../components/parameters/case_id.yaml' - $ref: '../components/parameters/space_id.yaml' - $ref: '../components/parameters/page_index.yaml' - example: "1" - $ref: '../components/parameters/page_size.yaml' - $ref: '../components/parameters/sort_order.yaml' - $ref: '../components/parameters/user_action_types.yaml' diff --git a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts index 1cc22c0799702..90b0d3b18908f 100644 --- a/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts +++ b/x-pack/plugins/cases/public/client/helpers/can_use_cases.ts @@ -40,10 +40,19 @@ export const canUseCases = acc.update = acc.update || userCapabilitiesForOwner.update; acc.delete = acc.delete || userCapabilitiesForOwner.delete; acc.push = acc.push || userCapabilitiesForOwner.push; + acc.connectors = acc.connectors || userCapabilitiesForOwner.connectors; + acc.settings = acc.settings || userCapabilitiesForOwner.settings; + const allFromAcc = - acc.create && acc.read && acc.update && acc.delete && acc.push && acc.connectors; + acc.create && + acc.read && + acc.update && + acc.delete && + acc.push && + acc.connectors && + acc.settings; + acc.all = acc.all || userCapabilitiesForOwner.all || allFromAcc; - acc.connectors = acc.connectors || userCapabilitiesForOwner.connectors; return acc; }, @@ -55,6 +64,7 @@ export const canUseCases = delete: false, push: false, connectors: false, + settings: false, } ); diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts index a3f741f373032..ce374243b10b2 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.test.ts @@ -17,6 +17,7 @@ describe('getUICapabilities', () => { "delete": false, "push": false, "read": false, + "settings": false, "update": false, } `); @@ -31,6 +32,7 @@ describe('getUICapabilities', () => { "delete": false, "push": false, "read": false, + "settings": false, "update": false, } `); @@ -45,6 +47,7 @@ describe('getUICapabilities', () => { "delete": false, "push": false, "read": false, + "settings": false, "update": false, } `); @@ -68,6 +71,7 @@ describe('getUICapabilities', () => { "delete": false, "push": false, "read": false, + "settings": false, "update": false, } `); @@ -82,6 +86,7 @@ describe('getUICapabilities', () => { "delete": false, "push": false, "read": false, + "settings": false, "update": false, } `); @@ -105,6 +110,7 @@ describe('getUICapabilities', () => { "delete": true, "push": true, "read": true, + "settings": false, "update": true, } `); @@ -113,23 +119,65 @@ describe('getUICapabilities', () => { it('returns false for the all field when cases_connectors is false', () => { expect( getUICapabilities({ - create_cases: false, + create_cases: true, read_cases: true, update_cases: true, delete_cases: true, push_cases: true, cases_connectors: false, + cases_settings: true, }) ).toMatchInlineSnapshot(` Object { "all": false, "connectors": false, - "create": false, + "create": true, + "delete": true, + "push": true, + "read": true, + "settings": true, + "update": true, + } + `); + }); + + it('returns false for the all field when cases_settings is false', () => { + expect( + getUICapabilities({ + create_cases: true, + read_cases: true, + update_cases: true, + delete_cases: true, + push_cases: true, + cases_connectors: true, + cases_settings: false, + }) + ).toMatchInlineSnapshot(` + Object { + "all": false, + "connectors": true, + "create": true, "delete": true, "push": true, "read": true, + "settings": false, "update": true, } `); }); + + it('returns true for cases_settings when it is set to true in the ui capabilities', () => { + expect(getUICapabilities({ cases_settings: true })).toMatchInlineSnapshot(` + Object { + "all": false, + "connectors": false, + "create": false, + "delete": false, + "push": false, + "read": false, + "settings": true, + "update": false, + } + `); + }); }); diff --git a/x-pack/plugins/cases/public/client/helpers/capabilities.ts b/x-pack/plugins/cases/public/client/helpers/capabilities.ts index 278512fef623c..9be5b5f05f646 100644 --- a/x-pack/plugins/cases/public/client/helpers/capabilities.ts +++ b/x-pack/plugins/cases/public/client/helpers/capabilities.ts @@ -8,6 +8,7 @@ import type { CasesPermissions } from '../../../common'; import { CASES_CONNECTORS_CAPABILITY, + CASES_SETTINGS_CAPABILITY, CREATE_CASES_CAPABILITY, DELETE_CASES_CAPABILITY, PUSH_CASES_CAPABILITY, @@ -24,7 +25,9 @@ export const getUICapabilities = ( const deletePriv = !!featureCapabilities?.[DELETE_CASES_CAPABILITY]; const push = !!featureCapabilities?.[PUSH_CASES_CAPABILITY]; const connectors = !!featureCapabilities?.[CASES_CONNECTORS_CAPABILITY]; - const all = create && read && update && deletePriv && push && connectors; + const settings = !!featureCapabilities?.[CASES_SETTINGS_CAPABILITY]; + + const all = create && read && update && deletePriv && push && connectors && settings; return { all, @@ -34,5 +37,6 @@ export const getUICapabilities = ( delete: deletePriv, push, connectors, + settings, }; }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts index c540824b1ebb5..39b4d3d1edc76 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/cases/public/common/lib/kibana/hooks.ts @@ -10,7 +10,7 @@ import moment from 'moment-timezone'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import type { NavigateToAppOptions } from '@kbn/core/public'; import { getUICapabilities } from '../../../client/helpers/capabilities'; import { convertToCamelCase } from '../../../api/utils'; @@ -194,6 +194,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { delete: permissions.delete, push: permissions.push, connectors: permissions.connectors, + settings: permissions.settings, }, visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show }, dashboard: { @@ -215,6 +216,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => { permissions.delete, permissions.push, permissions.connectors, + permissions.settings, ] ); }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx index 31ea452874c28..195c1f433a8e7 100644 --- a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx +++ b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.tsx @@ -75,6 +75,7 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta delete_cases: true, push_cases: true, cases_connectors: true, + cases_settings: true, }, visualize: { save: true, show: true }, dashboard: { show: true, createNew: true }, diff --git a/x-pack/plugins/cases/public/common/mock/permissions.ts b/x-pack/plugins/cases/public/common/mock/permissions.ts index 4d68e9d36c776..fce274cd7f338 100644 --- a/x-pack/plugins/cases/public/common/mock/permissions.ts +++ b/x-pack/plugins/cases/public/common/mock/permissions.ts @@ -16,7 +16,9 @@ export const noCasesPermissions = () => delete: false, push: false, connectors: false, + settings: false, }); + export const readCasesPermissions = () => buildCasesPermissions({ read: true, @@ -25,6 +27,7 @@ export const readCasesPermissions = () => delete: false, push: false, connectors: true, + settings: false, }); export const noCreateCasesPermissions = () => buildCasesPermissions({ create: false }); export const noUpdateCasesPermissions = () => buildCasesPermissions({ update: false }); @@ -34,6 +37,7 @@ export const writeCasesPermissions = () => buildCasesPermissions({ read: false } export const onlyDeleteCasesPermission = () => buildCasesPermissions({ read: false, create: false, update: false, delete: true, push: false }); export const noConnectorsCasePermission = () => buildCasesPermissions({ connectors: false }); +export const noCasesSettingsPermission = () => buildCasesPermissions({ settings: false }); export const buildCasesPermissions = (overrides: Partial> = {}) => { const create = overrides.create ?? true; @@ -42,7 +46,8 @@ export const buildCasesPermissions = (overrides: Partial delete_cases: false, push_cases: false, cases_connectors: false, + cases_settings: false, }); export const readCasesCapabilities = () => buildCasesCapabilities({ @@ -71,6 +78,7 @@ export const readCasesCapabilities = () => update_cases: false, delete_cases: false, push_cases: false, + cases_settings: false, }); export const writeCasesCapabilities = () => { return buildCasesCapabilities({ @@ -86,5 +94,6 @@ export const buildCasesCapabilities = (overrides?: Partial) = delete_cases: overrides?.delete_cases ?? true, push_cases: overrides?.push_cases ?? true, cases_connectors: overrides?.cases_connectors ?? true, + cases_settings: overrides?.cases_settings ?? true, }; }; diff --git a/x-pack/plugins/cases/public/components/all_cases/columns_popover.tsx b/x-pack/plugins/cases/public/components/all_cases/columns_popover.tsx index e95afcc159e98..d16b1f20059a9 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns_popover.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns_popover.tsx @@ -7,6 +7,7 @@ import type { ChangeEvent } from 'react'; import React, { useCallback, useMemo, useState } from 'react'; +import { css } from '@emotion/react'; import type { DropResult } from '@elastic/eui'; @@ -128,7 +129,11 @@ export const ColumnsPopover: React.FC = ({ diff --git a/x-pack/plugins/cases/public/components/all_cases/header.test.tsx b/x-pack/plugins/cases/public/components/all_cases/header.test.tsx index 08bd228b32e11..333f330394442 100644 --- a/x-pack/plugins/cases/public/components/all_cases/header.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/header.test.tsx @@ -46,9 +46,9 @@ describe('CasesTableHeader', () => { expect(result.getByTestId('configure-case-button')).toBeInTheDocument(); }); - it('does not display the configure button when the user does not have update privileges', () => { + it('does not display the configure button when the user does not have settings privileges', () => { appMockRender = createAppMockRenderer({ - permissions: buildCasesPermissions({ update: false }), + permissions: buildCasesPermissions({ settings: false }), }); const result = appMockRender.render(); diff --git a/x-pack/plugins/cases/public/components/all_cases/nav_buttons.test.tsx b/x-pack/plugins/cases/public/components/all_cases/nav_buttons.test.tsx new file mode 100644 index 0000000000000..b825f5c27f2eb --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/nav_buttons.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { screen } from '@testing-library/react'; +import type { AppMockRenderer } from '../../common/mock'; +import { + createAppMockRenderer, + noCasesSettingsPermission, + noCreateCasesPermissions, + buildCasesPermissions, +} from '../../common/mock'; +import { NavButtons } from './nav_buttons'; + +describe('NavButtons', () => { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + appMockRenderer = createAppMockRenderer(); + }); + + it('shows the configure case button', () => { + appMockRenderer.render(); + + expect(screen.getByTestId('configure-case-button')).toBeInTheDocument(); + }); + + it('does not render the case create button with no create permissions', () => { + appMockRenderer = createAppMockRenderer({ permissions: noCreateCasesPermissions() }); + appMockRenderer.render(); + + expect(screen.queryByTestId('createNewCaseBtn')).not.toBeInTheDocument(); + }); + + it('does not render the case configure button with no settings permissions', () => { + appMockRenderer = createAppMockRenderer({ permissions: noCasesSettingsPermission() }); + appMockRenderer.render(); + + expect(screen.queryByTestId('configure-case-button')).not.toBeInTheDocument(); + }); + + it('does not render any button with no create and no settings permissions', () => { + appMockRenderer = createAppMockRenderer({ + permissions: buildCasesPermissions({ create: false, settings: false }), + }); + appMockRenderer.render(); + + expect(screen.queryByTestId('createNewCaseBtn')).not.toBeInTheDocument(); + expect(screen.queryByTestId('configure-case-button')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/nav_buttons.tsx b/x-pack/plugins/cases/public/components/all_cases/nav_buttons.tsx index aafb287eed869..05febf94f431f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/nav_buttons.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/nav_buttons.tsx @@ -43,14 +43,14 @@ export const NavButtons: FunctionComponent = ({ actionsErrors }) => { [navigateToCreateCase] ); - if (!permissions.create && !permissions.update) { + if (!permissions.create && !permissions.settings) { return null; } return ( - {permissions.update && ( + {permissions.settings && ( { "align": "right", "name": "Actions", "render": [Function], + "width": "100px", }, } `); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx index ea43f79b4954e..70a163bcd69a0 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx @@ -244,6 +244,7 @@ export const useActions = ({ disableActions }: UseBulkActionsProps): UseBulkActi ); }, + width: '100px', } : null, }; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx index b61e7548b089e..0577dcabeb67d 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx @@ -90,13 +90,12 @@ describe('useCasesColumns ', () => { "field": "assignees", "name": "Assignees", "render": [Function], - "width": "180px", }, Object { "field": "tags", "name": "Tags", "render": [Function], - "width": "15%", + "width": "12%", }, Object { "align": "right", @@ -110,13 +109,14 @@ describe('useCasesColumns ', () => { "field": "totalComment", "name": "Comments", "render": [Function], + "width": "90px", }, Object { "field": "category", "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -139,13 +139,13 @@ describe('useCasesColumns ', () => { Object { "name": "External incident", "render": [Function], - "width": undefined, }, Object { "field": "status", "name": "Status", "render": [Function], "sortable": true, + "width": "110px", }, Object { "field": "severity", @@ -158,6 +158,7 @@ describe('useCasesColumns ', () => { "align": "right", "name": "Actions", "render": [Function], + "width": "100px", }, ], "isLoadingColumns": false, @@ -190,13 +191,12 @@ describe('useCasesColumns ', () => { "field": "assignees", "name": "Assignees", "render": [Function], - "width": "180px", }, Object { "field": "tags", "name": "Tags", "render": [Function], - "width": "15%", + "width": "12%", }, Object { "align": "right", @@ -210,13 +210,14 @@ describe('useCasesColumns ', () => { "field": "totalComment", "name": "Comments", "render": [Function], + "width": "90px", }, Object { "field": "category", "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -233,13 +234,13 @@ describe('useCasesColumns ', () => { Object { "name": "External incident", "render": [Function], - "width": undefined, }, Object { "field": "status", "name": "Status", "render": [Function], "sortable": true, + "width": "110px", }, Object { "field": "severity", @@ -252,6 +253,7 @@ describe('useCasesColumns ', () => { "align": "right", "name": "Actions", "render": [Function], + "width": "100px", }, ], "isLoadingColumns": false, @@ -288,7 +290,7 @@ describe('useCasesColumns ', () => { "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -336,7 +338,7 @@ describe('useCasesColumns ', () => { "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -384,7 +386,7 @@ describe('useCasesColumns ', () => { "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -430,7 +432,7 @@ describe('useCasesColumns ', () => { "field": "tags", "name": "Tags", "render": [Function], - "width": "15%", + "width": "12%", }, Object { "align": "right", @@ -444,13 +446,14 @@ describe('useCasesColumns ', () => { "field": "totalComment", "name": "Comments", "render": [Function], + "width": "90px", }, Object { "field": "category", "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -467,13 +470,13 @@ describe('useCasesColumns ', () => { Object { "name": "External incident", "render": [Function], - "width": undefined, }, Object { "field": "status", "name": "Status", "render": [Function], "sortable": true, + "width": "110px", }, Object { "field": "severity", @@ -536,7 +539,7 @@ describe('useCasesColumns ', () => { "field": "tags", "name": "Tags", "render": [Function], - "width": "15%", + "width": "12%", }, Object { "align": "right", @@ -550,13 +553,14 @@ describe('useCasesColumns ', () => { "field": "totalComment", "name": "Comments", "render": [Function], + "width": "90px", }, Object { "field": "category", "name": "Category", "render": [Function], "sortable": true, - "width": "100px", + "width": "120px", }, Object { "field": "createdAt", @@ -573,13 +577,13 @@ describe('useCasesColumns ', () => { Object { "name": "External incident", "render": [Function], - "width": undefined, }, Object { "field": "status", "name": "Status", "render": [Function], "sortable": true, + "width": "110px", }, Object { "field": "severity", diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx index 6ef5dcae9fe6b..de053bfb27fab 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx @@ -137,7 +137,6 @@ export const useCasesColumns = ({ render: (assignees: CaseUI['assignees']) => ( ), - width: '180px', }, tags: { field: casesColumnsConfig.tags.field, @@ -184,7 +183,7 @@ export const useCasesColumns = ({ } return getEmptyCellValue(); }, - width: '15%', + width: '12%', }, totalAlerts: { field: casesColumnsConfig.totalAlerts.field, @@ -204,6 +203,7 @@ export const useCasesColumns = ({ totalComment != null ? renderStringField(`${totalComment}`, `case-table-column-commentCount`) : getEmptyCellValue(), + width: '90px', }, category: { field: casesColumnsConfig.category.field, @@ -217,7 +217,7 @@ export const useCasesColumns = ({ } return getEmptyCellValue(); }, - width: '100px', + width: '120px', }, closedAt: { field: casesColumnsConfig.closedAt.field, @@ -273,7 +273,6 @@ export const useCasesColumns = ({ } return getEmptyCellValue(); }, - width: isSelectorView ? '80px' : undefined, }, status: { field: casesColumnsConfig.status.field, @@ -286,6 +285,7 @@ export const useCasesColumns = ({ return getEmptyCellValue(); }, + width: '110px', }, severity: { field: casesColumnsConfig.severity.field, diff --git a/x-pack/plugins/cases/public/components/app/routes.test.tsx b/x-pack/plugins/cases/public/components/app/routes.test.tsx index 53dc47de90733..9c435e2b163ba 100644 --- a/x-pack/plugins/cases/public/components/app/routes.test.tsx +++ b/x-pack/plugins/cases/public/components/app/routes.test.tsx @@ -11,8 +11,8 @@ import type { MemoryRouterProps } from 'react-router'; import { render, screen, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { + noCasesSettingsPermission, noCreateCasesPermissions, - noUpdateCasesPermissions, readCasesPermissions, TestProviders, } from '../../common/mock'; @@ -96,14 +96,14 @@ describe('Cases routes', () => { }); }); - describe('Configure cases', () => { - it('navigates to the configure cases page', () => { + describe('Cases settings', () => { + it('navigates to the cases settings page', () => { renderWithRouter(['/cases/configure']); expect(screen.getByText('Settings')).toBeInTheDocument(); }); - it('shows the no privileges page if the user does not have update privileges', () => { - renderWithRouter(['/cases/configure'], noUpdateCasesPermissions()); + it('shows the no privileges page if the user does not have settings privileges', () => { + renderWithRouter(['/cases/configure'], noCasesSettingsPermission()); expect(screen.getByText('Privileges required')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/app/routes.tsx b/x-pack/plugins/cases/public/components/app/routes.tsx index 7f4e35fc4ac81..27bf536b11ab8 100644 --- a/x-pack/plugins/cases/public/components/app/routes.tsx +++ b/x-pack/plugins/cases/public/components/app/routes.tsx @@ -71,7 +71,7 @@ const CasesRoutesComponent: React.FC = ({ - {permissions.update ? ( + {permissions.settings ? ( ) : ( diff --git a/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.test.tsx index f9b913af4d429..b7c87f3356d38 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.test.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.test.tsx @@ -37,11 +37,21 @@ describe('CustomFieldsList', () => { it('shows CustomFieldsList correctly', async () => { appMockRender.render(); - expect(screen.getByTestId('custom-fields-list')).toBeInTheDocument(); + expect(await screen.findByTestId('custom-fields-list')).toBeInTheDocument(); - for (const field of customFieldsConfigurationMock) { - expect(screen.getByTestId(`custom-field-${field.key}-${field.type}`)).toBeInTheDocument(); - } + expect( + await screen.findByTestId( + `custom-field-${customFieldsConfigurationMock[0].key}-${customFieldsConfigurationMock[0].type}` + ) + ).toBeInTheDocument(); + expect(await screen.findByText('Text')).toBeInTheDocument(); + expect(await screen.findByText('Required')).toBeInTheDocument(); + expect( + await screen.findByTestId( + `custom-field-${customFieldsConfigurationMock[1].key}-${customFieldsConfigurationMock[1].type}` + ) + ).toBeInTheDocument(); + expect(await screen.findByText('Toggle')).toBeInTheDocument(); }); it('shows single CustomFieldsList correctly', async () => { @@ -49,16 +59,21 @@ describe('CustomFieldsList', () => { ); - const list = screen.getByTestId('custom-fields-list'); + const list = await screen.findByTestId('custom-fields-list'); expect(list).toBeInTheDocument(); expect( - screen.getByTestId( + await screen.findByTestId( `custom-field-${customFieldsConfigurationMock[0].key}-${customFieldsConfigurationMock[0].type}` ) ).toBeInTheDocument(); + expect(await screen.findByText('Text')).toBeInTheDocument(); + expect(await screen.findByText('Required')).toBeInTheDocument(); + expect( + await within(list).findByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-edit`) + ).toBeInTheDocument(); expect( - within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`) + await within(list).findByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`) ).toBeInTheDocument(); }); @@ -76,10 +91,12 @@ describe('CustomFieldsList', () => { it('shows confirmation modal when deleting a field ', async () => { appMockRender.render(); - const list = screen.getByTestId('custom-fields-list'); + const list = await screen.findByTestId('custom-fields-list'); userEvent.click( - within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`) + await within(list).findByTestId( + `${customFieldsConfigurationMock[0].key}-custom-field-delete` + ) ); expect(await screen.findByTestId('confirm-delete-custom-field-modal')).toBeInTheDocument(); @@ -88,15 +105,17 @@ describe('CustomFieldsList', () => { it('calls onDeleteCustomField when confirm', async () => { appMockRender.render(); - const list = screen.getByTestId('custom-fields-list'); + const list = await screen.findByTestId('custom-fields-list'); userEvent.click( - within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`) + await within(list).findByTestId( + `${customFieldsConfigurationMock[0].key}-custom-field-delete` + ) ); expect(await screen.findByTestId('confirm-delete-custom-field-modal')).toBeInTheDocument(); - userEvent.click(screen.getByText('Delete')); + userEvent.click(await screen.findByText('Delete')); await waitFor(() => { expect(screen.queryByTestId('confirm-delete-custom-field-modal')).not.toBeInTheDocument(); @@ -109,15 +128,17 @@ describe('CustomFieldsList', () => { it('does not call onDeleteCustomField when cancel', async () => { appMockRender.render(); - const list = screen.getByTestId('custom-fields-list'); + const list = await screen.findByTestId('custom-fields-list'); userEvent.click( - within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`) + await within(list).findByTestId( + `${customFieldsConfigurationMock[0].key}-custom-field-delete` + ) ); expect(await screen.findByTestId('confirm-delete-custom-field-modal')).toBeInTheDocument(); - userEvent.click(screen.getByText('Cancel')); + userEvent.click(await screen.findByText('Cancel')); await waitFor(() => { expect(screen.queryByTestId('confirm-delete-custom-field-modal')).not.toBeInTheDocument(); @@ -134,10 +155,10 @@ describe('CustomFieldsList', () => { it('calls onEditCustomField correctly', async () => { appMockRender.render(); - const list = screen.getByTestId('custom-fields-list'); + const list = await screen.findByTestId('custom-fields-list'); userEvent.click( - within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-edit`) + await within(list).findByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-edit`) ); await waitFor(() => { diff --git a/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.tsx b/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.tsx index 649b0ec5d339f..cfccb53e48db3 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/custom_fields_list/index.tsx @@ -13,7 +13,10 @@ import { EuiSpacer, EuiText, EuiButtonIcon, + useEuiTheme, + EuiBadge, } from '@elastic/eui'; +import * as i18n from '../translations'; import type { CustomFieldTypes, CustomFieldsConfiguration } from '../../../../common/types/domain'; import { builderMap } from '../builder'; @@ -28,6 +31,7 @@ export interface Props { const CustomFieldsListComponent: React.FC = (props) => { const { customFields, onDeleteCustomField, onEditCustomField } = props; const [selectedItem, setSelectedItem] = useState(null); + const { euiTheme } = useEuiTheme(); const renderTypeLabel = (type?: CustomFieldTypes) => { const createdBuilder = type && builderMap[type]; @@ -69,7 +73,12 @@ const CustomFieldsListComponent: React.FC = (props) => {

    {customField.label}

    - {renderTypeLabel(customField.type)} + + {renderTypeLabel(customField.type)} + + {customField.required && ( + {i18n.REQUIRED} + )}
    diff --git a/x-pack/plugins/cases/public/components/custom_fields/translations.ts b/x-pack/plugins/cases/public/components/custom_fields/translations.ts index ac7f99f191373..a5ac6da2fa5f3 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/translations.ts +++ b/x-pack/plugins/cases/public/components/custom_fields/translations.ts @@ -66,6 +66,10 @@ export const FIELD_OPTION_REQUIRED = i18n.translate( } ); +export const REQUIRED = i18n.translate('xpack.cases.customFields.required', { + defaultMessage: 'Required', +}); + export const REQUIRED_FIELD = (fieldName: string): string => i18n.translate('xpack.cases.customFields.requiredField', { values: { fieldName }, diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.test.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.test.tsx index 868e9be03ff6f..2dfd0d188f5bb 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.test.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.test.tsx @@ -6,14 +6,18 @@ */ import React from 'react'; -import { mount } from 'enzyme'; +import { screen } from '@testing-library/react'; import type { CallOutProps } from './callout'; import { CallOut } from './callout'; import { CLOSED_CASE_PUSH_ERROR_ID } from './types'; -import { TestProviders } from '../../../common/mock'; +import type { AppMockRenderer } from '../../../common/mock'; +import { noCasesSettingsPermission, createAppMockRenderer } from '../../../common/mock'; +import userEvent from '@testing-library/user-event'; describe('Callout', () => { + let appMockRenderer: AppMockRenderer; + const handleButtonClick = jest.fn(); const defaultProps: CallOutProps = { id: 'md5-hex', @@ -31,50 +35,19 @@ describe('Callout', () => { beforeEach(() => { jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); }); it('It renders the callout', () => { - const wrapper = mount(); - expect(wrapper.find(`[data-test-subj="case-callout-md5-hex"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="callout-messages-md5-hex"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="callout-onclick-md5-hex"]`).exists()).toBeTruthy(); + appMockRenderer.render(); + expect(screen.getByTestId('case-callout-md5-hex')).toBeInTheDocument(); + expect(screen.getByTestId('callout-messages-md5-hex')).toBeInTheDocument(); + expect(screen.getByTestId('callout-onclick-md5-hex')).toBeInTheDocument(); }); it('does not shows any messages when the list is empty', () => { - const wrapper = mount(); - expect(wrapper.find(`[data-test-subj="callout-messages-md5-hex"]`).exists()).toBeFalsy(); - }); - - it('transform the button color correctly - primary', () => { - const wrapper = mount(); - const className = - wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).first().prop('className') ?? - ''; - expect(className.includes('primary')).toBeTruthy(); - }); - - it('transform the button color correctly - success', () => { - const wrapper = mount(); - const className = - wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).first().prop('className') ?? - ''; - expect(className.includes('success')).toBeTruthy(); - }); - - it('transform the button color correctly - warning', () => { - const wrapper = mount(); - const className = - wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).first().prop('className') ?? - ''; - expect(className.includes('warning')).toBeTruthy(); - }); - - it('transform the button color correctly - danger', () => { - const wrapper = mount(); - const className = - wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).first().prop('className') ?? - ''; - expect(className.includes('danger')).toBeTruthy(); + appMockRenderer.render(); + expect(screen.queryByTestId('callout-messages-md5-hex')).not.toBeInTheDocument(); }); it('does not show the button when case is closed error is present', () => { @@ -89,15 +62,9 @@ describe('Callout', () => { ], }; - const wrapper = mount( - - - - ); + appMockRenderer.render(); - expect(wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).exists()).toEqual( - false - ); + expect(screen.queryByTestId('callout-onclick-md5-hex')).not.toBeInTheDocument(); }); it('does not show the button when license error is present', () => { @@ -106,22 +73,27 @@ describe('Callout', () => { hasLicenseError: true, }; - const wrapper = mount( - - - - ); + appMockRenderer.render(); + + expect(screen.queryByTestId('callout-onclick-md5-hex')).not.toBeInTheDocument(); + }); + + it('does not show the button with no settings permissions', () => { + appMockRenderer = createAppMockRenderer({ permissions: noCasesSettingsPermission() }); - expect(wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).exists()).toEqual( - false - ); + appMockRenderer.render(); + + expect(screen.queryByTestId('callout-onclick-md5-hex')).not.toBeInTheDocument(); }); // use this for storage if we ever want to bring that back it('onClick passes id and type', () => { - const wrapper = mount(); - expect(wrapper.find(`[data-test-subj="callout-onclick-md5-hex"]`).exists()).toBeTruthy(); - wrapper.find(`button[data-test-subj="callout-onclick-md5-hex"]`).simulate('click'); + appMockRenderer.render(); + + expect(screen.getByTestId('callout-onclick-md5-hex')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('callout-onclick-md5-hex')); + expect(handleButtonClick.mock.calls[0][1]).toEqual('md5-hex'); expect(handleButtonClick.mock.calls[0][2]).toEqual('primary'); }); diff --git a/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.tsx b/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.tsx index ffd19f8366252..c94fbb826df48 100644 --- a/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.tsx +++ b/x-pack/plugins/cases/public/components/use_push_to_service/callout/callout.tsx @@ -12,6 +12,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import type { ErrorMessage } from './types'; import { CLOSED_CASE_PUSH_ERROR_ID } from './types'; import * as i18n from './translations'; +import { useCasesContext } from '../../cases_context/use_cases_context'; export interface CallOutProps { handleButtonClick: ( @@ -32,6 +33,8 @@ const CallOutComponent = ({ type, hasLicenseError, }: CallOutProps) => { + const { permissions } = useCasesContext(); + const handleCallOut = useCallback( (e) => handleButtonClick(e, id, type), [handleButtonClick, id, type] @@ -57,7 +60,7 @@ const CallOutComponent = ({ size="s" > - {!isCaseClosed && !hasLicenseError && ( + {!isCaseClosed && !hasLicenseError && permissions.settings && ( = { update: false, delete: false, push: false, + connectors: false, + settings: false, }), getRuleIdFromEvent: jest.fn(), groupAlertsByRule: jest.fn(), diff --git a/x-pack/plugins/cases/server/features.ts b/x-pack/plugins/cases/server/features.ts index b44c3589ecd08..62276ad4fcc30 100644 --- a/x-pack/plugins/cases/server/features.ts +++ b/x-pack/plugins/cases/server/features.ts @@ -100,6 +100,33 @@ export const getCasesKibanaFeature = (): KibanaFeatureConfig => { }, ], }, + { + name: i18n.translate('xpack.cases.features.casesSettingsSubFeatureName', { + defaultMessage: 'Case Settings', + }), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cases_settings', + name: i18n.translate('xpack.cases.features.casesSettingsSubFeatureDetails', { + defaultMessage: 'Edit Case Settings', + }), + includeIn: 'all', + savedObject: { + all: [...filesSavedObjectTypes], + read: [...filesSavedObjectTypes], + }, + cases: { + settings: [APP_ID], + }, + ui: capabilities.settings, + }, + ], + }, + ], + }, ], }; }; diff --git a/x-pack/plugins/cases/server/services/user_profiles/index.ts b/x-pack/plugins/cases/server/services/user_profiles/index.ts index ab8a9d8cf7d5b..7808cf74b0112 100644 --- a/x-pack/plugins/cases/server/services/user_profiles/index.ts +++ b/x-pack/plugins/cases/server/services/user_profiles/index.ts @@ -151,9 +151,7 @@ export class UserProfileService { private static buildRequiredPrivileges(owners: string[], security: SecurityPluginStart) { const privileges: string[] = []; for (const owner of owners) { - for (const operation of [Operations.updateCase.name, Operations.getCase.name]) { - privileges.push(security.authz.actions.cases.get(owner, operation)); - } + privileges.push(security.authz.actions.cases.get(owner, Operations.getCase.name)); } return privileges; diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/endpoints_modal.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/connection_details_modal.tsx similarity index 91% rename from x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/endpoints_modal.tsx rename to x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/connection_details_modal.tsx index 7c6b23d352f1a..d7be45e1db244 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/endpoints_modal.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/connection_details_modal.tsx @@ -22,7 +22,7 @@ interface Props { share: SharePluginStart; } -export const EndpointsModal = ({ core, share, cloud, docLinks, closeModal }: Props) => { +export const ConnectionDetailsModal = ({ core, share, cloud, docLinks, closeModal }: Props) => { return ( diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx index 15270c5876214..05ea7b9ce30cd 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx @@ -13,7 +13,7 @@ import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import { EndpointsModal } from './endpoints_modal'; +import { ConnectionDetailsModal } from './connection_details_modal'; export const createHelpMenuLinks = ({ docLinks, @@ -50,15 +50,15 @@ export const createHelpMenuLinks = ({ href: docLinks.links.kibana.feedback, }, { - title: i18n.translate('xpack.cloudLinks.helpMenuLinks.endpoints', { - defaultMessage: 'Endpoints', + title: i18n.translate('xpack.cloudLinks.helpMenuLinks.connectionDetails', { + defaultMessage: 'Connection details', }), iconType: 'console', - dataTestSubj: 'endpointsHelpLink', + dataTestSubj: 'connectionDetailsHelpLink', onClick: () => { const modal = overlays.openModal( toMountPoint( - { "title": "Give feedback", }, Object { - "dataTestSubj": "endpointsHelpLink", + "dataTestSubj": "connectionDetailsHelpLink", "iconType": "console", "onClick": [Function], - "title": "Endpoints", + "title": "Connection details", }, ], ] @@ -168,10 +168,10 @@ describe('maybeAddCloudLinks', () => { "title": "Give feedback", }, Object { - "dataTestSubj": "endpointsHelpLink", + "dataTestSubj": "connectionDetailsHelpLink", "iconType": "console", "onClick": [Function], - "title": "Endpoints", + "title": "Connection details", }, ], ] diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 1cc356cbfd5e3..d2fffa9d26a2b 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -10,7 +10,6 @@ import { VulnSeverity, AwsCredentialsTypeFieldMap, GcpCredentialsTypeFieldMap, - AzureCredentialsTypeFieldMap, } from './types'; export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status'; @@ -161,7 +160,25 @@ export const GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP: GcpCredentialsTypeFieldMap = { 'credentials-json': ['gcp.credentials.json'], }; -export const AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP: AzureCredentialsTypeFieldMap = { - manual: [], +export const AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP = { arm_template: [], + service_principal_with_client_secret: [ + 'azure.credentials.tenant_id', + 'azure.credentials.client_id', + 'azure.credentials.client_secret', + ], + service_principal_with_client_certificate: [ + 'azure.credentials.tenant_id', + 'azure.credentials.client_id', + 'azure.credentials.client_certificate_path', + 'azure.credentials.client_certificate_password', + ], + service_principal_with_client_username_and_password: [ + 'azure.credentials.tenant_id', + 'azure.credentials.client_id', + 'azure.credentials.client_username', + 'azure.credentials.client_password', + ], + managed_identity: [], + manual: [], }; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 092129a6762e2..bfce821222e29 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -30,7 +30,13 @@ export type GcpCredentialsTypeFieldMap = { [key in GcpCredentialsType]: string[]; }; -export type AzureCredentialsType = 'arm_template' | 'manual'; +export type AzureCredentialsType = + | 'arm_template' + | 'service_principal_with_client_secret' + | 'service_principal_with_client_certificate' + | 'service_principal_with_client_username_and_password' + | 'managed_identity' + | 'manual'; export type AzureCredentialsTypeFieldMap = { [key in AzureCredentialsType]: string[]; diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts index 85815e780b062..ba8e6f9813832 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -124,7 +124,7 @@ export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePacka const azureCredentialType: AzureCredentialsType | undefined = enabledInput?.streams?.[0].vars?.['azure.credentials.type']?.value; - if (awsCredentialType || gcpCredentialType) { + if (awsCredentialType || gcpCredentialType || azureCredentialType) { let credsToKeep: string[] = [' ']; let credFields: string[] = [' ']; if (awsCredentialType) { diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/index.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/index.ts new file mode 100644 index 0000000000000..60a917846dc99 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_cloud_posture_data_table'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/use_cloud_posture_data_table.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/use_cloud_posture_data_table.ts new file mode 100644 index 0000000000000..b7b928a208c0a --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/use_cloud_posture_data_table.ts @@ -0,0 +1,158 @@ +/* + * Copyright 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 { Dispatch, SetStateAction, useCallback } from 'react'; +import { type DataView } from '@kbn/data-views-plugin/common'; +import { BoolQuery, Filter } from '@kbn/es-query'; +import { CriteriaWithPagination } from '@elastic/eui'; +import { DataTableRecord } from '@kbn/discover-utils/types'; +import { useUrlQuery } from '../use_url_query'; +import { usePageSize } from '../use_page_size'; +import { getDefaultQuery, useBaseEsQuery, usePersistedQuery } from './utils'; +import { LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY } from '../../constants'; +import { FindingsBaseURLQuery } from '../../types'; + +type URLQuery = FindingsBaseURLQuery & Record; + +type SortOrder = [string, string]; + +export interface CloudPostureDataTableResult { + setUrlQuery: (query: Record) => void; + sort: SortOrder[]; + filters: Filter[]; + query: { bool: BoolQuery }; + queryError?: Error; + pageIndex: number; + urlQuery: URLQuery; + setTableOptions: (options: CriteriaWithPagination) => void; + handleUpdateQuery: (query: URLQuery) => void; + pageSize: number; + setPageSize: Dispatch>; + onChangeItemsPerPage: (newPageSize: number) => void; + onChangePage: (newPageIndex: number) => void; + onSort: (sort: string[][]) => void; + onResetFilters: () => void; + columnsLocalStorageKey: string; + getRowsFromPages: (data: Array<{ page: DataTableRecord[] }> | undefined) => DataTableRecord[]; +} + +/* + Hook for managing common table state and methods for the Cloud Posture DataTable +*/ +export const useCloudPostureDataTable = ({ + defaultQuery = getDefaultQuery, + dataView, + paginationLocalStorageKey, + columnsLocalStorageKey, + nonPersistedFilters, +}: { + defaultQuery?: (params: FindingsBaseURLQuery) => FindingsBaseURLQuery; + dataView: DataView; + paginationLocalStorageKey: string; + columnsLocalStorageKey?: string; + nonPersistedFilters?: Filter[]; +}): CloudPostureDataTableResult => { + const getPersistedDefaultQuery = usePersistedQuery(defaultQuery); + const { urlQuery, setUrlQuery } = useUrlQuery(getPersistedDefaultQuery); + const { pageSize, setPageSize } = usePageSize(paginationLocalStorageKey); + + const onChangeItemsPerPage = useCallback( + (newPageSize) => { + setPageSize(newPageSize); + setUrlQuery({ + pageIndex: 0, + pageSize: newPageSize, + }); + }, + [setPageSize, setUrlQuery] + ); + + const onResetFilters = useCallback(() => { + setUrlQuery({ + pageIndex: 0, + filters: [], + query: { + query: '', + language: 'kuery', + }, + }); + }, [setUrlQuery]); + + const onChangePage = useCallback( + (newPageIndex) => { + setUrlQuery({ + pageIndex: newPageIndex, + }); + }, + [setUrlQuery] + ); + + const onSort = useCallback( + (sort) => { + setUrlQuery({ + sort, + }); + }, + [setUrlQuery] + ); + + const setTableOptions = useCallback( + ({ page, sort }) => { + setPageSize(page.size); + setUrlQuery({ + sort, + pageIndex: page.index, + }); + }, + [setUrlQuery, setPageSize] + ); + + /** + * Page URL query to ES query + */ + const baseEsQuery = useBaseEsQuery({ + dataView, + filters: urlQuery.filters, + query: urlQuery.query, + ...(nonPersistedFilters ? { nonPersistedFilters } : {}), + }); + + const handleUpdateQuery = useCallback( + (query) => { + setUrlQuery({ ...query, pageIndex: 0 }); + }, + [setUrlQuery] + ); + + const getRowsFromPages = (data: Array<{ page: DataTableRecord[] }> | undefined) => + data + ?.map(({ page }: { page: DataTableRecord[] }) => { + return page; + }) + .flat() || []; + + const queryError = baseEsQuery instanceof Error ? baseEsQuery : undefined; + + return { + setUrlQuery, + sort: urlQuery.sort, + filters: urlQuery.filters, + query: baseEsQuery.query, + queryError, + pageIndex: urlQuery.pageIndex, + urlQuery, + setTableOptions, + handleUpdateQuery, + pageSize, + setPageSize, + onChangeItemsPerPage, + onChangePage, + onSort, + onResetFilters, + columnsLocalStorageKey: columnsLocalStorageKey || LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY, + getRowsFromPages, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts new file mode 100644 index 0000000000000..e86d2a77589b0 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts @@ -0,0 +1,128 @@ +/* + * Copyright 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 { useEffect, useCallback, useMemo } from 'react'; +import { buildEsQuery, EsQueryConfig } from '@kbn/es-query'; +import type { EuiBasicTableProps, Pagination } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { type Query } from '@kbn/es-query'; +import { useKibana } from '../use_kibana'; +import type { + FindingsBaseESQueryConfig, + FindingsBaseProps, + FindingsBaseURLQuery, +} from '../../types'; + +const getBaseQuery = ({ + dataView, + query, + filters, + config, +}: FindingsBaseURLQuery & FindingsBaseProps & FindingsBaseESQueryConfig) => { + try { + return { + query: buildEsQuery(dataView, query, filters, config), // will throw for malformed query + }; + } catch (error) { + throw new Error(error); + } +}; + +type TablePagination = NonNullable['pagination']>; + +export const getPaginationTableParams = ( + params: TablePagination & Pick, 'pageIndex' | 'pageSize'>, + pageSizeOptions = [10, 25, 100], + showPerPageOptions = true +): Required => ({ + ...params, + pageSizeOptions, + showPerPageOptions, +}); + +export const getPaginationQuery = ({ + pageIndex, + pageSize, +}: Required>) => ({ + from: pageIndex * pageSize, + size: pageSize, +}); + +export const useBaseEsQuery = ({ + dataView, + filters = [], + query, + nonPersistedFilters, +}: FindingsBaseURLQuery & FindingsBaseProps) => { + const { + notifications: { toasts }, + data: { + query: { filterManager, queryString }, + }, + uiSettings, + } = useKibana().services; + const allowLeadingWildcards = uiSettings.get('query:allowLeadingWildcards'); + const config: EsQueryConfig = useMemo(() => ({ allowLeadingWildcards }), [allowLeadingWildcards]); + const baseEsQuery = useMemo( + () => + getBaseQuery({ + dataView, + filters: filters.concat(nonPersistedFilters ?? []).flat(), + query, + config, + }), + [dataView, filters, nonPersistedFilters, query, config] + ); + + /** + * Sync filters with the URL query + */ + useEffect(() => { + filterManager.setAppFilters(filters); + queryString.setQuery(query); + }, [filters, filterManager, queryString, query]); + + const handleMalformedQueryError = () => { + const error = baseEsQuery instanceof Error ? baseEsQuery : undefined; + if (error) { + toasts.addError(error, { + title: i18n.translate('xpack.csp.findings.search.queryErrorToastMessage', { + defaultMessage: 'Query Error', + }), + toastLifeTimeMs: 1000 * 5, + }); + } + }; + + useEffect(handleMalformedQueryError, [baseEsQuery, toasts]); + + return baseEsQuery; +}; + +export const usePersistedQuery = (getter: ({ filters, query }: FindingsBaseURLQuery) => T) => { + const { + data: { + query: { filterManager, queryString }, + }, + } = useKibana().services; + + return useCallback( + () => + getter({ + filters: filterManager.getAppFilters(), + query: queryString.getQuery() as Query, + }), + [getter, filterManager, queryString] + ); +}; + +export const getDefaultQuery = ({ query, filters }: any): any => ({ + query, + filters, + sort: { field: '@timestamp', direction: 'desc' }, + pageIndex: 0, +}); diff --git a/x-pack/plugins/cloud_security_posture/public/common/types.ts b/x-pack/plugins/cloud_security_posture/public/common/types.ts index 6ebfe7c7a0fa3..a4c26643293fd 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/types.ts @@ -14,6 +14,10 @@ export type FindingsGroupByKind = 'default' | 'resource'; export interface FindingsBaseURLQuery { query: Query; filters: Filter[]; + /** + * Filters that are part of the query but not persisted in the URL or in the Filter Manager + */ + nonPersistedFilters?: Filter[]; } export interface FindingsBaseProps { diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/additional_controls.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/additional_controls.tsx index 61a85e9993ecf..b1f79779e65e4 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/additional_controls.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/additional_controls.tsx @@ -10,13 +10,22 @@ import { EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; import { type DataView } from '@kbn/data-views-plugin/common'; import numeral from '@elastic/numeral'; import { FieldsSelectorModal } from './fields_selector'; -import { FindingsGroupBySelector } from '../../pages/configurations/layout/findings_group_by_selector'; import { useStyles } from './use_styles'; const formatNumber = (value: number) => { return value < 1000 ? value : numeral(value).format('0.0a'); }; +const GroupSelectorWrapper: React.FC = ({ children }) => { + const styles = useStyles(); + + return ( + + {children} + + ); +}; + export const AdditionalControls = ({ total, title, @@ -24,6 +33,7 @@ export const AdditionalControls = ({ columns, onAddColumn, onRemoveColumn, + groupSelectorComponent, }: { total: number; title: string; @@ -31,9 +41,8 @@ export const AdditionalControls = ({ columns: string[]; onAddColumn: (column: string) => void; onRemoveColumn: (column: string) => void; + groupSelectorComponent?: JSX.Element; }) => { - const styles = useStyles(); - const [isFieldSelectorModalVisible, setIsFieldSelectorModalVisible] = useState(false); const closeModal = () => setIsFieldSelectorModalVisible(false); @@ -66,9 +75,9 @@ export const AdditionalControls = ({ })} - - - + {groupSelectorComponent && ( + {groupSelectorComponent} + )} ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx index 9487b405fbba5..50e81a0a0c7ec 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx @@ -69,6 +69,15 @@ interface CloudSecurityDataGridProps { */ loadMore: () => void; 'data-test-subj'?: string; + /** + * This is the component that will be rendered in the group selector. + * This component will receive the current group and a function to change the group. + */ + groupSelectorComponent?: JSX.Element; + /** + * Height override for the data grid. + */ + height?: number; } export const CloudSecurityDataTable = ({ @@ -82,6 +91,8 @@ export const CloudSecurityDataTable = ({ loadMore, title, customCellRenderer, + groupSelectorComponent, + height, ...rest }: CloudSecurityDataGridProps) => { const { @@ -209,6 +220,7 @@ export const CloudSecurityDataTable = ({ columns={currentColumns} onAddColumn={onAddColumn} onRemoveColumn={onRemoveColumn} + groupSelectorComponent={groupSelectorComponent} /> ); @@ -216,7 +228,7 @@ export const CloudSecurityDataTable = ({ // Change the height of the grid to fit the page // If there are filters, leave space for the filter bar // Todo: Replace this component with EuiAutoSizer - height: `calc(100vh - ${filters.length > 0 ? 443 : 403}px)`, + height: height ?? `calc(100vh - ${filters?.length > 0 ? 443 : 403}px)`, }; const rowHeightState = 0; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/use_styles.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/use_styles.ts index b3b0fa1b172b1..3919e01af7753 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/use_styles.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/use_styles.ts @@ -23,9 +23,6 @@ export const useStyles = () => { border-bottom: none; margin-bottom: ${euiTheme.size.s}; border-top: none; - & .euiButtonEmpty { - font-weight: ${euiTheme.font.weight.bold}; - } } & .euiDataGrid--headerUnderline .euiDataGridHeaderCell { border-bottom: ${euiTheme.border.width.thick} solid ${euiTheme.colors.fullShade}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx new file mode 100644 index 0000000000000..067046ce5457a --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useGrouping } from '@kbn/securitysolution-grouping'; +import { ParsedGroupingAggregation } from '@kbn/securitysolution-grouping/src'; +import { Filter } from '@kbn/es-query'; +import React from 'react'; +import { css } from '@emotion/react'; + +interface CloudSecurityGroupingProps { + data: ParsedGroupingAggregation; + renderChildComponent: (groupFilter: Filter[]) => JSX.Element; + grouping: ReturnType; + activePageIndex: number; + isFetching: boolean; + pageSize: number; + onChangeGroupsItemsPerPage: (size: number) => void; + onChangeGroupsPage: (index: number) => void; + selectedGroup: string; +} + +export const CloudSecurityGrouping = ({ + data, + renderChildComponent, + grouping, + activePageIndex, + isFetching, + pageSize, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + selectedGroup, +}: CloudSecurityGroupingProps) => { + return ( +
    + {grouping.getGrouping({ + activePage: activePageIndex, + data, + groupingLevel: 0, + inspectButton: undefined, + isLoading: isFetching, + itemsPerPage: pageSize, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + renderChildComponent, + onGroupClose: () => {}, + selectedGroup, + takeActionItems: () => [], + })} +
    + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/index.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/index.ts new file mode 100644 index 0000000000000..35a321d06119d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { useCloudSecurityGrouping } from './use_cloud_security_grouping'; +export { CloudSecurityGrouping } from './cloud_security_grouping'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts new file mode 100644 index 0000000000000..d2783af516e35 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useEffect, useMemo, useState } from 'react'; +import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import * as uuid from 'uuid'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { useUrlQuery } from '../../common/hooks/use_url_query'; +import { + useBaseEsQuery, + usePersistedQuery, +} from '../../common/hooks/use_cloud_posture_data_table/utils'; +import { FindingsBaseURLQuery } from '../../common/types'; + +const DEFAULT_PAGE_SIZE = 10; +const GROUPING_ID = 'cspLatestFindings'; +const MAX_GROUPING_LEVELS = 1; + +/* + Utility hook to handle the grouping logic of the cloud security components +*/ +export const useCloudSecurityGrouping = ({ + dataView, + groupingTitle, + defaultGroupingOptions, + getDefaultQuery, + unit, +}: { + dataView: DataView; + groupingTitle: string; + defaultGroupingOptions: Array<{ label: string; key: string }>; + getDefaultQuery: (params: FindingsBaseURLQuery) => FindingsBaseURLQuery; + unit: (count: number) => string; +}) => { + const getPersistedDefaultQuery = usePersistedQuery(getDefaultQuery); + const { urlQuery, setUrlQuery } = useUrlQuery(getPersistedDefaultQuery); + const [activePageIndex, setActivePageIndex] = useState(0); + const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); + + const { query } = useBaseEsQuery({ + dataView, + filters: urlQuery.filters, + query: urlQuery.query, + }); + + /** + * Reset the active page when the filters or query change + * This is needed because the active page is not automatically reset when the filters or query change + */ + useEffect(() => { + setActivePageIndex(0); + }, [urlQuery.filters, urlQuery.query]); + + const grouping = useGrouping({ + componentProps: { + unit, + }, + defaultGroupingOptions, + fields: dataView.fields, + groupingId: GROUPING_ID, + maxGroupingLevels: MAX_GROUPING_LEVELS, + title: groupingTitle, + onGroupChange: () => { + setActivePageIndex(0); + }, + }); + + const selectedGroup = grouping.selectedGroups[0]; + + // This is recommended by the grouping component to cover an edge case + // where the selectedGroup has multiple values + const uniqueValue = useMemo(() => `${selectedGroup}-${uuid.v4()}`, [selectedGroup]); + + const isNoneSelected = isNoneGroup(grouping.selectedGroups); + + const onChangeGroupsItemsPerPage = (size: number) => { + setActivePageIndex(0); + setPageSize(size); + }; + + const onChangeGroupsPage = (index: number) => setActivePageIndex(index); + + return { + activePageIndex, + grouping, + pageSize, + query, + selectedGroup, + setUrlQuery, + uniqueValue, + isNoneSelected, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/empty_state.tsx b/x-pack/plugins/cloud_security_posture/public/components/empty_state.tsx index 9c38e635062f7..43f39023c9c32 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/empty_state.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/empty_state.tsx @@ -30,7 +30,9 @@ export const EmptyState = ({ && > .euiEmptyPrompt__main { gap: ${euiTheme.size.xl}; } - margin-top: ${euiTheme.size.xxxl}}; + && { + margin-top: ${euiTheme.size.xxxl}}; + } `} data-test-subj={EMPTY_STATE_TEST_SUBJ} icon={ diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx index 19fe841e3b01b..ab8770b20f0cf 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx @@ -5,7 +5,18 @@ * 2.0. */ import React, { useEffect } from 'react'; -import { EuiLink, EuiSpacer, EuiText, EuiTitle, EuiCallOut, EuiHorizontalRule } from '@elastic/eui'; +import { + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, + EuiCallOut, + EuiHorizontalRule, + EuiFormRow, + EuiSelect, + EuiFieldPassword, + EuiFieldText, +} from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -14,8 +25,13 @@ import { i18n } from '@kbn/i18n'; import semverValid from 'semver/functions/valid'; import semverCoerce from 'semver/functions/coerce'; import semverLt from 'semver/functions/lt'; +import { + AzureOptions, + getAzureCredentialsFormManualOptions, +} from './get_azure_credentials_form_options'; +import { AzureCredentialsType } from '../../../../common/types'; import { SetupFormat, useAzureCredentialsForm } from './hooks'; -import { NewPackagePolicyPostureInput } from '../utils'; +import { getPosturePolicy, NewPackagePolicyPostureInput } from '../utils'; import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group'; interface AzureSetupInfoContentProps { @@ -161,7 +177,31 @@ const ArmTemplateSetup = ({ ); }; -const ManualSetup = ({ integrationLink }: { integrationLink: string }) => { +const AzureCredentialTypeSelector = ({ + type, + onChange, +}: { + onChange(type: AzureCredentialsType): void; + type: AzureCredentialsType; +}) => ( + + { + onChange(optionElem.target.value as AzureCredentialsType); + }} + /> + +); + +const TemporaryManualSetup = ({ integrationLink }: { integrationLink: string }) => { return ( <> @@ -206,6 +246,53 @@ const ManualSetup = ({ integrationLink }: { integrationLink: string }) => { }; const AZURE_MINIMUM_PACKAGE_VERSION = '1.6.0'; +const AZURE_MANUAL_FIELDS_PACKAGE_VERSION = '1.7.0'; + +export const getDefaultAzureManualCredentialType = (packageInfo: PackageInfo) => { + const packageSemanticVersion = semverValid(packageInfo.version); + const cleanPackageVersion = semverCoerce(packageSemanticVersion) || ''; + + const isPackageVersionValidForManualFields = !semverLt( + cleanPackageVersion, + AZURE_MANUAL_FIELDS_PACKAGE_VERSION + ); + + return isPackageVersionValidForManualFields ? 'managed_identity' : 'manual'; +}; + +const AzureInputVarFields = ({ + fields, + onChange, +}: { + fields: Array; + onChange: (key: string, value: string) => void; +}) => ( +
    + {fields.map((field) => ( + + <> + {field.type === 'password' && ( + onChange(field.id, event.target.value)} + /> + )} + {field.type === 'text' && ( + onChange(field.id, event.target.value)} + /> + )} + + + ))} +
    +); export const AzureCredentialsForm = ({ input, @@ -216,15 +303,22 @@ export const AzureCredentialsForm = ({ setIsValid, disabled, }: Props) => { - const { setupFormat, onSetupFormatChange, integrationLink, hasArmTemplateUrl } = - useAzureCredentialsForm({ - newPolicy, - input, - packageInfo, - onChange, - setIsValid, - updatePolicy, - }); + const { + group, + fields, + azureCredentialsType, + setupFormat, + onSetupFormatChange, + integrationLink, + hasArmTemplateUrl, + } = useAzureCredentialsForm({ + newPolicy, + input, + packageInfo, + onChange, + setIsValid, + updatePolicy, + }); useEffect(() => { if (!setupFormat) { @@ -238,6 +332,10 @@ export const AzureCredentialsForm = ({ cleanPackageVersion, AZURE_MINIMUM_PACKAGE_VERSION ); + const isPackageVersionValidForManualFields = !semverLt( + cleanPackageVersion, + AZURE_MANUAL_FIELDS_PACKAGE_VERSION + ); useEffect(() => { setIsValid(isPackageVersionValidForAzure); @@ -280,8 +378,52 @@ export const AzureCredentialsForm = ({ {setupFormat === AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE && ( )} - {setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && ( - + {setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && !isPackageVersionValidForManualFields && ( + + )} + {setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && isPackageVersionValidForManualFields && ( + <> + { + updatePolicy( + getPosturePolicy(newPolicy, input.type, { + 'azure.credentials.type': { value: optionId }, + }) + ); + }} + /> + + { + updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })); + }} + /> + + {group.info} + + + + {i18n.translate('xpack.csp.azureIntegration.documentationLinkText', { + defaultMessage: 'documentation', + })} + + ), + }} + /> + + )} diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx index 5a074ad4c4ebc..455fe9352ba00 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx @@ -8,18 +8,32 @@ import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiText } from '@elastic/eui'; import { AzureCredentialsType } from '../../../../common/types'; export type AzureCredentialsFields = Record; export interface AzureOptionValue { label: string; - info: React.ReactNode; + info?: React.ReactNode; fields: AzureCredentialsFields; } export type AzureOptions = Record; +export const getAzureCredentialsFormManualOptions = (): Array<{ + value: AzureCredentialsType; + text: string; +}> => { + return Object.entries(getAzureCredentialsFormOptions()) + .map(([key, value]) => ({ + value: key as AzureCredentialsType, + text: value.label, + })) + .filter(({ value }) => value !== 'arm_template'); +}; + export const getInputVarsFields = (input: NewPackagePolicyInput, fields: AzureCredentialsFields) => Object.entries(input.streams[0].vars || {}) .filter(([id]) => id in fields) @@ -33,19 +47,102 @@ export const getInputVarsFields = (input: NewPackagePolicyInput, fields: AzureCr } as const; }); -export const DEFAULT_AZURE_MANUAL_CREDENTIALS_TYPE = 'manual'; +const I18N_TENANT_ID = i18n.translate('xpack.csp.azureIntegration.tenantIdLabel', { + defaultMessage: 'Tenant ID', +}); + +const I18N_CLIENT_ID = i18n.translate('xpack.csp.azureIntegration.clientIdLabel', { + defaultMessage: 'Client ID', +}); export const getAzureCredentialsFormOptions = (): AzureOptions => ({ + managed_identity: { + label: i18n.translate('xpack.csp.azureIntegration.credentialType.managedIdentityLabel', { + defaultMessage: 'Managed Identity', + }), + info: ( + + + + ), + fields: {}, + }, arm_template: { label: 'ARM Template', info: [], fields: {}, }, + service_principal_with_client_secret: { + label: i18n.translate('xpack.csp.azureIntegration.servicePrincipalWithClientSecretLabel', { + defaultMessage: 'Service principal with Client Secret', + }), + fields: { + 'azure.credentials.tenant_id': { label: I18N_TENANT_ID }, + 'azure.credentials.client_id': { label: I18N_CLIENT_ID }, + 'azure.credentials.client_secret': { + type: 'password', + label: i18n.translate('xpack.csp.azureIntegration.clientSecretLabel', { + defaultMessage: 'Client Secret', + }), + }, + }, + }, + service_principal_with_client_certificate: { + label: i18n.translate('xpack.csp.azureIntegration.servicePrincipalWithClientCertificateLabel', { + defaultMessage: 'Service principal with Client Certificate', + }), + fields: { + 'azure.credentials.tenant_id': { label: I18N_TENANT_ID }, + 'azure.credentials.client_id': { label: I18N_CLIENT_ID }, + 'azure.credentials.client_certificate_path': { + label: i18n.translate('xpack.csp.azureIntegration.clientCertificatePathLabel', { + defaultMessage: 'Client Certificate Path', + }), + }, + 'azure.credentials.client_certificate_password': { + type: 'password', + label: i18n.translate('xpack.csp.azureIntegration.clientCertificatePasswordLabel', { + defaultMessage: 'Client Certificate Password', + }), + }, + }, + }, + service_principal_with_client_username_and_password: { + label: i18n.translate( + 'xpack.csp.azureIntegration.servicePrincipalWithClientUsernameAndPasswordLabel', + { defaultMessage: 'Service principal with Client Username and Password' } + ), + fields: { + 'azure.credentials.tenant_id': { label: I18N_TENANT_ID }, + 'azure.credentials.client_id': { label: I18N_CLIENT_ID }, + 'azure.credentials.client_username': { + label: i18n.translate('xpack.csp.azureIntegration.clientUsernameLabel', { + defaultMessage: 'Client Username', + }), + }, + 'azure.credentials.client_password': { + type: 'password', + label: i18n.translate('xpack.csp.azureIntegration.clientPasswordLabel', { + defaultMessage: 'Client Password', + }), + }, + }, + }, manual: { label: i18n.translate('xpack.csp.azureIntegration.credentialType.manualLabel', { defaultMessage: 'Manual', }), - info: [], + info: ( + + + + ), fields: {}, }, }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts index 011c39cf8038e..b68828be58117 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts @@ -7,7 +7,10 @@ import { useEffect, useRef } from 'react'; import { NewPackagePolicy, PackageInfo } from '@kbn/fleet-plugin/common'; -import { AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE } from './azure_credentials_form'; +import { + AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE, + getDefaultAzureManualCredentialType, +} from './azure_credentials_form'; import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants'; import { getArmTemplateUrlFromCspmPackage, @@ -15,14 +18,28 @@ import { NewPackagePolicyPostureInput, } from '../utils'; import { - DEFAULT_AZURE_MANUAL_CREDENTIALS_TYPE, getAzureCredentialsFormOptions, getInputVarsFields, } from './get_azure_credentials_form_options'; import { CLOUDBEAT_AZURE } from '../../../../common/constants'; import { AzureCredentialsType } from '../../../../common/types'; -export type SetupFormat = AzureCredentialsType; +export type SetupFormat = 'arm_template' | 'manual'; + +const getSetupFormatFromInput = ( + input: Extract, + hasArmTemplateUrl: boolean +): SetupFormat => { + const credentialsType = getAzureCredentialsType(input); + if (!credentialsType && hasArmTemplateUrl) { + return 'arm_template'; + } + if (credentialsType !== 'arm_template') { + return 'manual'; + } + + return 'arm_template'; +}; const getAzureCredentialsType = ( input: Extract @@ -107,7 +124,7 @@ export const useAzureCredentialsForm = ({ const hasArmTemplateUrl = !!getArmTemplateUrlFromCspmPackage(packageInfo); - const setupFormat = azureCredentialsType; + const setupFormat = getSetupFormatFromInput(input, hasArmTemplateUrl); const group = options[azureCredentialsType]; const fields = getInputVarsFields(input, group.fields); @@ -134,6 +151,8 @@ export const useAzureCredentialsForm = ({ setupFormat, }); + const defaultAzureManualCredentialType = getDefaultAzureManualCredentialType(packageInfo); + const onSetupFormatChange = (newSetupFormat: SetupFormat) => { if (newSetupFormat === AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE) { fieldsSnapshot.current = Object.fromEntries( @@ -155,7 +174,7 @@ export const useAzureCredentialsForm = ({ updatePolicy( getPosturePolicy(newPolicy, input.type, { 'azure.credentials.type': { - value: lastManualCredentialsType.current || DEFAULT_AZURE_MANUAL_CREDENTIALS_TYPE, + value: lastManualCredentialsType.current || defaultAzureManualCredentialType, type: 'text', }, ...fieldsSnapshot.current, 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 0cde5fe86ac6d..4c1b04a260df1 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 @@ -162,8 +162,15 @@ const getPolicyMock = ( }; const azureVarsMock = { + 'azure.credentials.type': { value: 'arm_template', type: 'text' }, 'azure.account_type': { type: 'text' }, - 'azure.credentials.type': { type: 'text' }, + 'azure.credentials.tenant_id': { type: 'text' }, + 'azure.credentials.client_id': { type: 'text' }, + 'azure.credentials.client_secret': { type: 'text' }, + 'azure.credentials.client_certificate_path': { type: 'text' }, + 'azure.credentials.client_certificate_password': { type: 'text' }, + 'azure.credentials.client_username': { type: 'text' }, + 'azure.credentials.client_password': { type: 'text' }, }; const dataStream = { type: 'logs', dataset: 'cloud_security_posture.findings' }; 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 b1880eeb0c7ef..d0c1f4d454b4f 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 @@ -1330,6 +1330,27 @@ describe('', () => { ).toBeInTheDocument(); }); + it(`doesnt render ${CLOUDBEAT_AZURE} Manual fields when version is not at least version 1.7.0`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'manual' }, + 'azure.account_type': { value: 'single-account' }, + }); + + const { queryByRole } = render( + + ); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + expect( + queryByRole('option', { name: 'Service principal with Client Secret', selected: true }) + ).not.toBeInTheDocument(); + }); + it(`selects default ${CLOUDBEAT_AZURE} fields`, () => { let policy = getMockPolicyAzure(); policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { @@ -1344,5 +1365,242 @@ describe('', () => { updatedPolicy: policy, }); }); + + it(`renders ${CLOUDBEAT_AZURE} Service Principal with Client Secret fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_secret' }, + }); + + const { getByLabelText, getByRole } = render( + + ); + + expect( + getByRole('option', { name: 'Service principal with Client Secret', selected: true }) + ).toBeInTheDocument(); + expect(getByLabelText('Tenant ID')).toBeInTheDocument(); + expect(getByLabelText('Client ID')).toBeInTheDocument(); + expect(getByLabelText('Client Secret')).toBeInTheDocument(); + }); + + it(`updates ${CLOUDBEAT_AZURE} Service Principal with Client Secret fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_secret' }, + }); + + const { rerender, getByLabelText } = render( + + ); + + userEvent.type(getByLabelText('Tenant ID'), 'a'); + + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.tenant_id': { value: 'a' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client ID'), 'b'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_id': { value: 'b' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client Secret'), 'c'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_secret': { value: 'c' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + }); + }); + + it(`renders Service principal with Client Certificate fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_certificate' }, + }); + + const { getByLabelText, getByRole } = render( + + ); + + expect( + getByRole('option', { name: 'Service principal with Client Certificate', selected: true }) + ).toBeInTheDocument(); + expect(getByLabelText('Tenant ID')).toBeInTheDocument(); + expect(getByLabelText('Client ID')).toBeInTheDocument(); + expect(getByLabelText('Client Certificate Path')).toBeInTheDocument(); + expect(getByLabelText('Client Certificate Password')).toBeInTheDocument(); + }); + + it(`updates Service principal with Client Certificate fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_certificate' }, + }); + + const { rerender, getByLabelText } = render( + + ); + + userEvent.type(getByLabelText('Tenant ID'), 'a'); + + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.tenant_id': { value: 'a' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client ID'), 'b'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_id': { value: 'b' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client Certificate Path'), 'c'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_certificate_path': { value: 'c' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client Certificate Password'), 'd'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_certificate_password': { value: 'd' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + }); + + it(`renders Service principal with Client Username and Password fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_username_and_password' }, + }); + + const { getByLabelText, getByRole } = render( + + ); + + expect( + getByRole('option', { + name: 'Service principal with Client Username and Password', + selected: true, + }) + ).toBeInTheDocument(); + expect(getByLabelText('Tenant ID')).toBeInTheDocument(); + expect(getByLabelText('Client ID')).toBeInTheDocument(); + expect(getByLabelText('Client Username')).toBeInTheDocument(); + expect(getByLabelText('Client Password')).toBeInTheDocument(); + }); + + it(`updates Service principal with Client Username and Password fields`, () => { + let policy = getMockPolicyAzure(); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_username_and_password' }, + }); + + const { rerender, getByLabelText } = render( + + ); + + userEvent.type(getByLabelText('Tenant ID'), 'a'); + + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.tenant_id': { value: 'a' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client ID'), 'b'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_id': { value: 'b' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client Username'), 'c'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_username': { value: 'c' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); + + rerender( + + ); + + userEvent.type(getByLabelText('Client Password'), 'd'); + policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { + 'azure.credentials.client_password': { value: 'd' }, + }); + + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: policy, + }); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/subscription_not_allowed.tsx b/x-pack/plugins/cloud_security_posture/public/components/subscription_not_allowed.tsx index 644455e5a2a68..a2d8f4fe32c0b 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/subscription_not_allowed.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/subscription_not_allowed.tsx @@ -6,15 +6,8 @@ */ import React from 'react'; -import { EuiEmptyPrompt, EuiPageSection } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLink, EuiPageSection } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SubscriptionLink } from '@kbn/subscription-tracking'; -import type { SubscriptionContextData } from '@kbn/subscription-tracking'; - -const subscriptionContext: SubscriptionContextData = { - feature: 'cloud-security-posture', - source: 'security__cloud-security-posture', -}; export const SubscriptionNotAllowed = ({ licenseManagementLocator, @@ -41,12 +34,12 @@ export const SubscriptionNotAllowed = ({ defaultMessage="To use these cloud security features, you must {link}." values={{ link: ( - + - + ), }} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx index 50d5493387466..a0170705b1ec5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx @@ -21,11 +21,7 @@ import type { PosturePolicyTemplate, } from '../../../../common/types'; import { RisksTable } from '../compliance_charts/risks_table'; -import { - NavFilter, - useNavigateFindings, - useNavigateFindingsByResource, -} from '../../../common/hooks/use_navigate_findings'; +import { NavFilter, useNavigateFindings } from '../../../common/hooks/use_navigate_findings'; import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE, @@ -55,7 +51,6 @@ export const SummarySection = ({ complianceData: ComplianceDashboardData; }) => { const navToFindings = useNavigateFindings(); - const navToFindingsByResource = useNavigateFindingsByResource(); const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE); const kspmIntegrationLink = useCspIntegrationLink(KSPM_POLICY_TEMPLATE); @@ -121,7 +116,7 @@ export const SummarySection = ({ { - navToFindingsByResource(getPolicyTemplateQuery(dashboardType)); + navToFindings(getPolicyTemplateQuery(dashboardType)); }} > {i18n.translate( @@ -138,7 +133,7 @@ export const SummarySection = ({ cspmIntegrationLink, dashboardType, kspmIntegrationLink, - navToFindingsByResource, + navToFindings, ] ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts new file mode 100644 index 0000000000000..54884856fccf1 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { FindingsBaseURLQuery } from '../../../common/types'; +import { CloudSecurityDefaultColumn } from '../../../components/cloud_security_data_table'; + +export const FINDINGS_UNIT = (totalCount: number) => + i18n.translate('xpack.csp.findings.unit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {finding} other {findings}}`, + }); + +export const defaultGroupingOptions = [ + { + label: i18n.translate('xpack.csp.findings.latestFindings.groupByResource', { + defaultMessage: 'Resource', + }), + key: 'resource.name', + }, + { + label: i18n.translate('xpack.csp.findings.latestFindings.groupByRuleName', { + defaultMessage: 'Rule name', + }), + key: 'rule.name', + }, + { + label: i18n.translate('xpack.csp.findings.latestFindings.groupByCloudAccount', { + defaultMessage: 'Cloud account', + }), + key: 'cloud.account.name', + }, + { + label: i18n.translate('xpack.csp.findings.latestFindings.groupByKubernetesCluster', { + defaultMessage: 'Kubernetes cluster', + }), + key: 'orchestrator.cluster.name', + }, +]; + +export const groupingTitle = i18n.translate('xpack.csp.findings.latestFindings.groupBy', { + defaultMessage: 'Group findings by', +}); + +export const DEFAULT_TABLE_HEIGHT = 512; + +export const getDefaultQuery = ({ + query, + filters, +}: FindingsBaseURLQuery): FindingsBaseURLQuery & { + sort: string[][]; +} => ({ + query, + filters, + sort: [['@timestamp', 'desc']], +}); + +export const defaultColumns: CloudSecurityDefaultColumn[] = [ + { id: 'result.evaluation', width: 80 }, + { id: 'resource.id' }, + { id: 'resource.name' }, + { id: 'resource.sub_type' }, + { id: 'rule.benchmark.rule_number' }, + { id: 'rule.name' }, + { id: 'rule.section' }, + { id: '@timestamp' }, +]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_container.tsx index 9abae7af4a211..613594c66e939 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_container.tsx @@ -4,180 +4,70 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo } from 'react'; -import { EuiDataGridCellValueElementProps, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { DataTableRecord } from '@kbn/discover-utils/types'; -import { Filter, Query } from '@kbn/es-query'; -import { TimestampTableCell } from '../../../components/timestamp_table_cell'; -import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; -import type { Evaluation } from '../../../../common/types'; -import type { FindingsBaseProps, FindingsBaseURLQuery } from '../../../common/types'; +import React, { useCallback } from 'react'; +import { Filter } from '@kbn/es-query'; +import { defaultLoadingRenderer } from '../../../components/cloud_posture_page'; +import { CloudSecurityGrouping } from '../../../components/cloud_security_grouping'; +import type { FindingsBaseProps } from '../../../common/types'; import { FindingsSearchBar } from '../layout/findings_search_bar'; -import * as TEST_SUBJECTS from '../test_subjects'; -import { useLatestFindings } from './use_latest_findings'; -import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; -import { getFilters } from '../utils/utils'; -import { ErrorCallout } from '../layout/error_callout'; -import { LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY } from '../../../common/constants'; -import { CspFinding } from '../../../../common/schemas/csp_finding'; -import { useCloudPostureTable } from '../../../common/hooks/use_cloud_posture_table'; -import { - CloudSecurityDataTable, - CloudSecurityDefaultColumn, -} from '../../../components/cloud_security_data_table'; -import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; - -const getDefaultQuery = ({ - query, - filters, -}: { - query: Query; - filters: Filter[]; -}): FindingsBaseURLQuery & { - sort: string[][]; -} => ({ - query, - filters, - sort: [['@timestamp', 'desc']], -}); - -const defaultColumns: CloudSecurityDefaultColumn[] = [ - { id: 'result.evaluation', width: 80 }, - { id: 'resource.id' }, - { id: 'resource.name' }, - { id: 'resource.sub_type' }, - { id: 'rule.benchmark.rule_number' }, - { id: 'rule.name' }, - { id: 'rule.section' }, - { id: '@timestamp' }, -]; - -/** - * Type Guard for checking if the given source is a CspFinding - */ -const isCspFinding = (source: Record | undefined): source is CspFinding => { - return source?.result?.evaluation !== undefined; -}; - -/** - * This Wrapper component renders the children if the given row is a CspFinding - * it uses React's Render Props pattern - */ -const CspFindingRenderer = ({ - row, - children, -}: { - row: DataTableRecord; - children: ({ finding }: { finding: CspFinding }) => JSX.Element; -}) => { - const source = row.raw._source; - const finding = isCspFinding(source) && (source as CspFinding); - if (!finding) return <>; - return children({ finding }); -}; - -/** - * Flyout component for the latest findings table - */ -const flyoutComponent = (row: DataTableRecord, onCloseFlyout: () => void): JSX.Element => { - return ( - - {({ finding }) => } - - ); -}; - -const columnsLocalStorageKey = 'cloudPosture:latestFindings:columns'; - -const title = i18n.translate('xpack.csp.findings.latestFindings.tableRowTypeLabel', { - defaultMessage: 'Findings', -}); - -const customCellRenderer = (rows: DataTableRecord[]) => ({ - 'result.evaluation': ({ rowIndex }: EuiDataGridCellValueElementProps) => ( - - {({ finding }) => } - - ), - '@timestamp': ({ rowIndex }: EuiDataGridCellValueElementProps) => ( - - {({ finding }) => } - - ), -}); +import { DEFAULT_TABLE_HEIGHT } from './constants'; +import { useLatestFindingsGrouping } from './use_latest_findings_grouping'; +import { LatestFindingsTable } from './latest_findings_table'; export const LatestFindingsContainer = ({ dataView }: FindingsBaseProps) => { - const cloudPostureTable = useCloudPostureTable({ - dataView, - paginationLocalStorageKey: LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY, - columnsLocalStorageKey, - defaultQuery: getDefaultQuery, - }); - - const { query, sort, queryError, setUrlQuery, filters, getRowsFromPages } = cloudPostureTable; + const renderChildComponent = useCallback( + (groupFilters: Filter[]) => { + return ( + + ); + }, + [dataView] + ); const { - data, - error: fetchError, + isGroupSelect, + groupData, + grouping, isFetching, - fetchNextPage, - } = useLatestFindings({ - query, - sort, - enabled: !queryError, - }); - - const rows = useMemo(() => getRowsFromPages(data?.pages), [data?.pages, getRowsFromPages]); - - const error = fetchError || queryError; - - const passed = data?.pages[0].count.passed || 0; - const failed = data?.pages[0].count.failed || 0; - const total = data?.pages[0].total || 0; - - const handleDistributionClick = (evaluation: Evaluation) => { - setUrlQuery({ - filters: getFilters({ - filters, - dataView, - field: 'result.evaluation', - value: evaluation, - negate: false, - }), - }); - }; + activePageIndex, + pageSize, + selectedGroup, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + setUrlQuery, + isGroupLoading, + } = useLatestFindingsGrouping({ dataView }); + + if (isGroupSelect) { + return isGroupLoading ? ( + defaultLoadingRenderer() + ) : ( +
    + + +
    + ); + } return ( - + <> - - {error && } - {!error && ( - <> - {total > 0 && ( - - )} - - - - )} - + + ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_table.tsx new file mode 100644 index 0000000000000..a66ce54fa99cc --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_table.tsx @@ -0,0 +1,148 @@ +/* + * Copyright 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 { Filter } from '@kbn/es-query'; +import { DataTableRecord } from '@kbn/discover-utils/types'; +import { i18n } from '@kbn/i18n'; +import { EuiDataGridCellValueElementProps, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import { FindingsBaseProps } from '../../../common/types'; +import * as TEST_SUBJECTS from '../test_subjects'; +import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; +import { ErrorCallout } from '../layout/error_callout'; +import { CloudSecurityDataTable } from '../../../components/cloud_security_data_table'; +import { getDefaultQuery, defaultColumns } from './constants'; +import { useLatestFindingsTable } from './use_latest_findings_table'; +import { TimestampTableCell } from '../../../components/timestamp_table_cell'; +import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; +import { CspFinding } from '../../../../common/schemas/csp_finding'; +import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; + +type LatestFindingsTableProps = FindingsBaseProps & { + groupSelectorComponent?: JSX.Element; + height?: number; + showDistributionBar?: boolean; + nonPersistedFilters?: Filter[]; +}; + +/** + * Type Guard for checking if the given source is a CspFinding + */ +const isCspFinding = (source: Record | undefined): source is CspFinding => { + return source?.result?.evaluation !== undefined; +}; + +/** + * This Wrapper component renders the children if the given row is a CspFinding + * it uses React's Render Props pattern + */ +const CspFindingRenderer = ({ + row, + children, +}: { + row: DataTableRecord; + children: ({ finding }: { finding: CspFinding }) => JSX.Element; +}) => { + const source = row.raw._source; + const finding = isCspFinding(source) && (source as CspFinding); + if (!finding) return <>; + return children({ finding }); +}; + +/** + * Flyout component for the latest findings table + */ +const flyoutComponent = (row: DataTableRecord, onCloseFlyout: () => void): JSX.Element => { + return ( + + {({ finding }) => } + + ); +}; + +const title = i18n.translate('xpack.csp.findings.latestFindings.tableRowTypeLabel', { + defaultMessage: 'Findings', +}); + +const customCellRenderer = (rows: DataTableRecord[]) => ({ + 'result.evaluation': ({ rowIndex }: EuiDataGridCellValueElementProps) => ( + + {({ finding }) => } + + ), + '@timestamp': ({ rowIndex }: EuiDataGridCellValueElementProps) => ( + + {({ finding }) => } + + ), +}); + +export const LatestFindingsTable = ({ + dataView, + groupSelectorComponent, + height, + showDistributionBar = true, + nonPersistedFilters, +}: LatestFindingsTableProps) => { + const { + cloudPostureTable, + rows, + error, + isFetching, + fetchNextPage, + passed, + failed, + total, + canShowDistributionBar, + onDistributionBarClick, + } = useLatestFindingsTable({ + dataView, + getDefaultQuery, + nonPersistedFilters, + showDistributionBar, + }); + + return ( + + {error ? ( + <> + + + + ) : ( + <> + {canShowDistributionBar && ( + <> + + + + + )} + + + )} + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx new file mode 100644 index 0000000000000..66b1faf5060c9 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IKibanaSearchResponse } from '@kbn/data-plugin/public'; +import { GroupingAggregation } from '@kbn/securitysolution-grouping'; +import { GenericBuckets, GroupingQuery } from '@kbn/securitysolution-grouping/src'; +import { useQuery } from '@tanstack/react-query'; +import { lastValueFrom } from 'rxjs'; +import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants'; +import { useKibana } from '../../../common/hooks/use_kibana'; +import { showErrorToast } from '../../../common/utils/show_error_toast'; + +// Elasticsearch returns `null` when a sub-aggregation cannot be computed +type NumberOrNull = number | null; + +export interface FindingsGroupingAggregation { + unitsCount?: { + value?: NumberOrNull; + }; + groupsCount?: { + value?: NumberOrNull; + }; + groupByFields?: { + buckets?: GenericBuckets[]; + }; +} + +export const getGroupedFindingsQuery = (query: GroupingQuery) => ({ + ...query, + index: CSP_LATEST_FINDINGS_DATA_VIEW, + size: 0, +}); + +export const useGroupedFindings = ({ + query, + enabled = true, +}: { + query: GroupingQuery; + enabled: boolean; +}) => { + const { + data, + notifications: { toasts }, + } = useKibana().services; + + return useQuery( + ['csp_grouped_findings', { query }], + async () => { + const { + rawResponse: { aggregations }, + } = await lastValueFrom( + data.search.search< + {}, + IKibanaSearchResponse< + SearchResponse<{}, GroupingAggregation> + > + >({ + params: getGroupedFindingsQuery(query), + }) + ); + + if (!aggregations) throw new Error('Failed to aggregate by, missing resource id'); + + return aggregations; + }, + { + onError: (err: Error) => showErrorToast(toasts, err), + enabled, + // This allows the UI to keep the previous data while the new data is being fetched + keepPreviousData: true, + } + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts index 9ce0292175839..0c0aee860d344 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts @@ -16,7 +16,10 @@ import { CspFinding } from '../../../../common/schemas/csp_finding'; import { useKibana } from '../../../common/hooks/use_kibana'; import type { FindingsBaseEsQuery } from '../../../common/types'; import { getAggregationCount, getFindingsCountAggQuery } from '../utils/utils'; -import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants'; +import { + CSP_LATEST_FINDINGS_DATA_VIEW, + LATEST_FINDINGS_RETENTION_POLICY, +} from '../../../../common/constants'; import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants'; import { showErrorToast } from '../../../common/utils/show_error_toast'; @@ -41,11 +44,27 @@ interface FindingsAggs { export const getFindingsQuery = ({ query, sort }: UseFindingsOptions, pageParam: any) => ({ index: CSP_LATEST_FINDINGS_DATA_VIEW, - query, sort: getMultiFieldsSort(sort), size: MAX_FINDINGS_TO_LOAD, aggs: getFindingsCountAggQuery(), ignore_unavailable: false, + query: { + ...query, + bool: { + ...query?.bool, + filter: [ + ...(query?.bool?.filter ?? []), + { + range: { + '@timestamp': { + gte: `now-${LATEST_FINDINGS_RETENTION_POLICY}`, + lte: 'now', + }, + }, + }, + ], + }, + }, ...(pageParam ? { search_after: pageParam } : {}), }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx new file mode 100644 index 0000000000000..137716967460f --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { getGroupingQuery } from '@kbn/securitysolution-grouping'; +import { parseGroupingQuery } from '@kbn/securitysolution-grouping/src'; +import { useMemo } from 'react'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { LATEST_FINDINGS_RETENTION_POLICY } from '../../../../common/constants'; +import { useGroupedFindings } from './use_grouped_findings'; +import { FINDINGS_UNIT, groupingTitle, defaultGroupingOptions, getDefaultQuery } from './constants'; +import { useCloudSecurityGrouping } from '../../../components/cloud_security_grouping'; + +/** + * Utility hook to get the latest findings grouping data + * for the findings page + */ +export const useLatestFindingsGrouping = ({ dataView }: { dataView: DataView }) => { + const { + activePageIndex, + grouping, + pageSize, + query, + selectedGroup, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + setUrlQuery, + uniqueValue, + isNoneSelected, + } = useCloudSecurityGrouping({ + dataView, + groupingTitle, + defaultGroupingOptions, + getDefaultQuery, + unit: FINDINGS_UNIT, + }); + + const groupingQuery = getGroupingQuery({ + additionalFilters: [query], + groupByField: selectedGroup, + uniqueValue, + from: `now-${LATEST_FINDINGS_RETENTION_POLICY}`, + to: 'now', + pageNumber: activePageIndex * pageSize, + size: pageSize, + sort: [{ _key: { order: 'asc' } }], + }); + + const { data, isFetching } = useGroupedFindings({ + query: groupingQuery, + enabled: !isNoneSelected, + }); + + const groupData = useMemo( + () => parseGroupingQuery(selectedGroup, uniqueValue, data), + [data, selectedGroup, uniqueValue] + ); + + return { + groupData, + grouping, + isFetching, + activePageIndex, + pageSize, + selectedGroup, + onChangeGroupsItemsPerPage, + onChangeGroupsPage, + setUrlQuery, + isGroupSelect: !isNoneSelected, + isGroupLoading: !data, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx new file mode 100644 index 0000000000000..2bcc895ddd4b0 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataView } from '@kbn/data-views-plugin/common'; +import { Filter } from '@kbn/es-query'; +import { useMemo } from 'react'; +import { FindingsBaseURLQuery } from '../../../common/types'; +import { Evaluation } from '../../../../common/types'; +import { LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY } from '../../../common/constants'; +import { useCloudPostureDataTable } from '../../../common/hooks/use_cloud_posture_data_table'; +import { getFilters } from '../utils/get_filters'; +import { useLatestFindings } from './use_latest_findings'; + +const columnsLocalStorageKey = 'cloudPosture:latestFindings:columns'; + +export const useLatestFindingsTable = ({ + dataView, + getDefaultQuery, + nonPersistedFilters, + showDistributionBar, +}: { + dataView: DataView; + getDefaultQuery: (params: FindingsBaseURLQuery) => FindingsBaseURLQuery; + nonPersistedFilters?: Filter[]; + showDistributionBar?: boolean; +}) => { + const cloudPostureTable = useCloudPostureDataTable({ + dataView, + paginationLocalStorageKey: LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY, + columnsLocalStorageKey, + defaultQuery: getDefaultQuery, + nonPersistedFilters, + }); + + const { query, sort, queryError, setUrlQuery, filters, getRowsFromPages } = cloudPostureTable; + + const { + data, + error: fetchError, + isFetching, + fetchNextPage, + } = useLatestFindings({ + query, + sort, + enabled: !queryError, + }); + + const rows = useMemo(() => getRowsFromPages(data?.pages), [data?.pages, getRowsFromPages]); + + const error = fetchError || queryError; + + const passed = data?.pages[0].count.passed || 0; + const failed = data?.pages[0].count.failed || 0; + const total = data?.pages[0].total || 0; + + const onDistributionBarClick = (evaluation: Evaluation) => { + setUrlQuery({ + filters: getFilters({ + filters, + dataView, + field: 'result.evaluation', + value: evaluation, + negate: false, + }), + }); + }; + + const canShowDistributionBar = showDistributionBar && total > 0; + + return { + cloudPostureTable, + rows, + error, + isFetching, + fetchNextPage, + passed, + failed, + total, + canShowDistributionBar, + onDistributionBarClick, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx index 3f89c934e5dd4..bdccb07851629 100755 --- a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx +++ b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx @@ -11,7 +11,6 @@ import { I18nProvider } from '@kbn/i18n-react'; // eslint-disable-next-line no-restricted-imports import { Router } from 'react-router-dom'; import { Route, Routes } from '@kbn/shared-ux-router'; -import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { coreMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; @@ -52,11 +51,9 @@ export const TestProvider: React.FC> = ({ - - - <>{children}} /> - - + + <>{children}} /> + diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 113ddcb92202a..48fc1a30594f5 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -50,7 +50,6 @@ "@kbn/share-plugin", "@kbn/core-http-server", "@kbn/core-http-browser", - "@kbn/subscription-tracking", "@kbn/discover-utils", "@kbn/unified-data-table", "@kbn/cell-actions", @@ -60,7 +59,8 @@ "@kbn/ui-actions-plugin", "@kbn/core-http-server-mocks", "@kbn/field-formats-plugin", - "@kbn/data-view-field-editor-plugin" + "@kbn/data-view-field-editor-plugin", + "@kbn/securitysolution-grouping" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js index e020950668a84..839aa48464bbf 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js @@ -309,7 +309,7 @@ describe('', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/100951 + // FLAKY: https://github.com/elastic/kibana/issues/142774 describe.skip('detail panel', () => { test('should open a detail panel when clicking on a follower index', async () => { expect(exists('followerIndexDetail')).toBe(false); @@ -372,7 +372,8 @@ describe('', () => { ); }); - test('should have a section to render the follower index shards stats', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/100951 + test.skip('should have a section to render the follower index shards stats', async () => { await actions.clickFollowerIndexAt(0); expect(exists('followerIndexDetail.shardsStatsSection')).toBe(true); diff --git a/x-pack/plugins/observability/public/utils/cases_permissions.ts b/x-pack/plugins/dataset_quality/common/constants.ts similarity index 63% rename from x-pack/plugins/observability/public/utils/cases_permissions.ts rename to x-pack/plugins/dataset_quality/common/constants.ts index 2b3ff9cfbaf54..febb7d2a0f9f2 100644 --- a/x-pack/plugins/observability/public/utils/cases_permissions.ts +++ b/x-pack/plugins/dataset_quality/common/constants.ts @@ -5,11 +5,5 @@ * 2.0. */ -export const noCasesPermissions = () => ({ - all: false, - create: false, - read: false, - update: false, - delete: false, - push: false, -}); +export const DATASET_QUALITY_APP_ID = 'dataset_quality'; +export const DATA_STREAMS_STATS_URL = '/internal/dataset_quality/data_streams/stats'; diff --git a/x-pack/plugins/dataset_quality/common/data_streams/index.ts b/x-pack/plugins/dataset_quality/common/data_streams/index.ts new file mode 100644 index 0000000000000..6cc0ccaa93a6d --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/data_streams/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './types'; diff --git a/x-pack/plugins/dataset_quality/server/types/api_types.ts b/x-pack/plugins/dataset_quality/common/data_streams/types.ts similarity index 82% rename from x-pack/plugins/dataset_quality/server/types/api_types.ts rename to x-pack/plugins/dataset_quality/common/data_streams/types.ts index 077cb94aa3493..1a47a0d7e5ac6 100644 --- a/x-pack/plugins/dataset_quality/server/types/api_types.ts +++ b/x-pack/plugins/dataset_quality/common/data_streams/types.ts @@ -16,7 +16,3 @@ export const dataStreamTypesRt = t.partial({ t.literal('profiling'), ]), }); - -export const sortOrderRt = t.type({ - sortOrder: t.union([t.literal('asc'), t.literal('desc')]), -}); diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts new file mode 100644 index 0000000000000..5fd2a2ffc1ffc --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts @@ -0,0 +1,44 @@ +/* + * Copyright 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 { Integration } from './integration'; +import { DataStreamStatType, IntegrationType } from './types'; + +export class DataStreamStat { + name: DataStreamStatType['name']; + title: string; + size?: DataStreamStatType['size']; + sizeBytes?: DataStreamStatType['size_bytes']; + lastActivity?: DataStreamStatType['last_activity']; + integration?: IntegrationType; + + private constructor(dataStreamStat: DataStreamStat) { + this.name = dataStreamStat.name; + this.title = dataStreamStat.title ?? dataStreamStat.name; + this.size = dataStreamStat.size; + this.sizeBytes = dataStreamStat.sizeBytes; + this.lastActivity = dataStreamStat.lastActivity; + this.integration = dataStreamStat.integration; + } + + public static create(dataStreamStat: DataStreamStatType) { + const [_type, dataset, namespace] = dataStreamStat.name.split('-'); + + const dataStreamStatProps = { + name: dataStreamStat.name, + title: `${dataset}-${namespace}`, + size: dataStreamStat.size, + sizeBytes: dataStreamStat.size_bytes, + lastActivity: dataStreamStat.last_activity, + integration: dataStreamStat.integration + ? Integration.create(dataStreamStat.integration) + : undefined, + }; + + return new DataStreamStat(dataStreamStatProps); + } +} diff --git a/x-pack/plugins/apm/common/utils/to_kuery_filter_format.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/errors.ts similarity index 54% rename from x-pack/plugins/apm/common/utils/to_kuery_filter_format.ts rename to x-pack/plugins/dataset_quality/common/data_streams_stats/errors.ts index 8e3169f4d07e2..de47f4cb8c39d 100644 --- a/x-pack/plugins/apm/common/utils/to_kuery_filter_format.ts +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/errors.ts @@ -5,10 +5,10 @@ * 2.0. */ -export function toKueryFilterFormat( - key: string, - values: string[], - separator: 'OR' | 'AND' = 'OR' -) { - return values.map((value) => `${key} : "${value}"`).join(` ${separator} `); +export class GetDataStreamsStatsError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + this.name = 'GetDataStreamsStatsError'; + } } diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/index.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/index.ts new file mode 100644 index 0000000000000..28c7b0a8c274f --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './types'; +export * from './errors'; diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/integration.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/integration.ts new file mode 100644 index 0000000000000..937efd407e6fc --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/integration.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IntegrationType } from './types'; + +export class Integration { + name: IntegrationType['name']; + title: IntegrationType['title']; + version: IntegrationType['version']; + icons?: IntegrationType['icons']; + + private constructor(integration: Integration) { + this.name = integration.name; + this.title = integration.title || integration.name; + this.version = integration.version || '1.0.0'; + this.icons = integration.icons; + } + + public static create(integration: IntegrationType) { + const integrationProps = { + ...integration, + title: integration.title || integration.name, + version: integration.version || '1.0.0', + }; + + return new Integration(integrationProps); + } +} diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts new file mode 100644 index 0000000000000..f9a202798fb37 --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/types.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 { APIClientRequestParamsOf, APIReturnType } from '../rest/create_call_dataset_quality_api'; +import { DataStreamStat } from './data_stream_stat'; + +export type GetDataStreamsStatsParams = + APIClientRequestParamsOf<`GET /internal/dataset_quality/data_streams/stats`>['params']; +export type GetDataStreamsStatsQuery = GetDataStreamsStatsParams['query']; +export type GetDataStreamsStatsResponse = + APIReturnType<`GET /internal/dataset_quality/data_streams/stats`>; +export type DataStreamStatServiceResponse = DataStreamStat[]; +export type DataStreamStatType = GetDataStreamsStatsResponse['dataStreamsStats'][0]; +export type IntegrationType = GetDataStreamsStatsResponse['integrations'][0]; diff --git a/x-pack/plugins/dataset_quality/common/index.ts b/x-pack/plugins/dataset_quality/common/index.ts index 2022b51685043..b015815eeaacc 100644 --- a/x-pack/plugins/dataset_quality/common/index.ts +++ b/x-pack/plugins/dataset_quality/common/index.ts @@ -7,3 +7,4 @@ export type { DatasetQualityConfig } from './plugin_config'; export type { FetchOptions } from './fetch_options'; +export type { APIClientRequestParamsOf, APIReturnType } from './rest'; diff --git a/x-pack/plugins/dataset_quality/public/services/rest/call_api.ts b/x-pack/plugins/dataset_quality/common/rest/call_api.ts similarity index 95% rename from x-pack/plugins/dataset_quality/public/services/rest/call_api.ts rename to x-pack/plugins/dataset_quality/common/rest/call_api.ts index 1b39bfc905730..a70e2f8d407e5 100644 --- a/x-pack/plugins/dataset_quality/public/services/rest/call_api.ts +++ b/x-pack/plugins/dataset_quality/common/rest/call_api.ts @@ -6,7 +6,7 @@ */ import { CoreSetup, CoreStart } from '@kbn/core/public'; -import { FetchOptions } from '../../../common'; +import { FetchOptions } from '..'; function getFetchOptions(fetchOptions: FetchOptions) { const { body, ...rest } = fetchOptions; diff --git a/x-pack/plugins/dataset_quality/public/services/rest/create_call_dataset_quality_api.ts b/x-pack/plugins/dataset_quality/common/rest/create_call_dataset_quality_api.ts similarity index 96% rename from x-pack/plugins/dataset_quality/public/services/rest/create_call_dataset_quality_api.ts rename to x-pack/plugins/dataset_quality/common/rest/create_call_dataset_quality_api.ts index 47d04b309817f..8e3929bb77e74 100644 --- a/x-pack/plugins/dataset_quality/public/services/rest/create_call_dataset_quality_api.ts +++ b/x-pack/plugins/dataset_quality/common/rest/create_call_dataset_quality_api.ts @@ -12,8 +12,8 @@ import type { RouteRepositoryClient, } from '@kbn/server-route-repository'; import { formatRequest } from '@kbn/server-route-repository'; -import { FetchOptions } from '../../../common'; -import type { APIEndpoint, DatasetQualityServerRouteRepository } from '../../../server/routes'; +import { FetchOptions } from '..'; +import type { APIEndpoint, DatasetQualityServerRouteRepository } from '../../server/routes'; import { CallApi, callApi } from './call_api'; export type DatasetQualityClientOptions = Omit< diff --git a/x-pack/plugins/dataset_quality/common/rest/index.ts b/x-pack/plugins/dataset_quality/common/rest/index.ts new file mode 100644 index 0000000000000..559acfe62e502 --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/rest/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './call_api'; +export * from './create_call_dataset_quality_api'; diff --git a/x-pack/plugins/dataset_quality/common/translations.ts b/x-pack/plugins/dataset_quality/common/translations.ts new file mode 100644 index 0000000000000..b26b7ca5c9029 --- /dev/null +++ b/x-pack/plugins/dataset_quality/common/translations.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 { i18n } from '@kbn/i18n'; + +export const datasetQualityAppTitle = i18n.translate('xpack.datasetQuality.appTitle', { + defaultMessage: 'Datasets', +}); + +export const onboardingLinkTitle = i18n.translate('xpack.datasetQuality.onboardingLinkTitle', { + defaultMessage: 'Add data', +}); + +export const noDatasetsDescription = i18n.translate('xpack.datasetQuality.noDatasetsDescription', { + defaultMessage: 'Try adjusting your time or filter.', +}); + +export const noDatasetsTitle = i18n.translate('xpack.datasetQuality.noDatasetsTitle', { + defaultMessage: 'There is no data to display.', +}); + +export const loadingDatasetsText = i18n.translate('xpack.datasetQuality.loadingDatasetsText', { + defaultMessage: 'Loading data', +}); + +export const tableSummaryAllText = i18n.translate('xpack.datasetQuality.tableSummaryAllText', { + defaultMessage: 'All', +}); + +export const tableSummaryOfText = i18n.translate('xpack.datasetQuality.tableSummaryOfText', { + defaultMessage: 'of', +}); diff --git a/x-pack/plugins/dataset_quality/kibana.jsonc b/x-pack/plugins/dataset_quality/kibana.jsonc index 133537a76d831..710d7e82890c7 100644 --- a/x-pack/plugins/dataset_quality/kibana.jsonc +++ b/x-pack/plugins/dataset_quality/kibana.jsonc @@ -8,7 +8,7 @@ "server": true, "browser": true, "configPath": ["xpack", "datasetQuality"], - "requiredPlugins": ["data", "kibanaReact", "kibanaUtils", "controls", "embeddable", "share"], + "requiredPlugins": ["data", "kibanaReact", "kibanaUtils", "controls", "embeddable", "share", "observabilityShared", "fleet", "fieldFormats"], "optionalPlugins": [], "requiredBundles": [], "extraPublicDirs": ["common"] diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx new file mode 100644 index 0000000000000..aa653a95d6220 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiBasicTableColumn, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { PackageIcon } from '@kbn/fleet-plugin/public'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { DataStreamStat } from '../../../common/data_streams_stats/data_stream_stat'; +import loggingIcon from '../../icons/logging.svg'; + +const nameColumnName = i18n.translate('xpack.datasetQuality.nameColumnName', { + defaultMessage: 'Dataset Name', +}); + +const sizeColumnName = i18n.translate('xpack.datasetQuality.sizeColumnName', { + defaultMessage: 'Size', +}); + +const lastActivityColumnName = i18n.translate('xpack.datasetQuality.lastActivityColumnName', { + defaultMessage: 'Last Activity', +}); + +export const getDatasetQualitTableColumns = ({ + fieldFormats, +}: { + fieldFormats: FieldFormatsStart; +}): Array> => { + return [ + { + name: nameColumnName, + field: 'title', + sortable: true, + render: (title: string, dataStreamStat: DataStreamStat) => { + const { integration } = dataStreamStat; + + return ( + + + {integration ? ( + + ) : ( + + )} + + {title} + + ); + }, + }, + { + name: sizeColumnName, + field: 'size', + sortable: true, + }, + { + name: lastActivityColumnName, + field: 'lastActivity', + render: (timestamp: number) => + fieldFormats + .getDefaultInstance(KBN_FIELD_TYPES.DATE, [ES_FIELD_TYPES.DATE]) + .convert(timestamp), + sortable: true, + }, + ]; +}; diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/context.ts b/x-pack/plugins/dataset_quality/public/components/dataset_quality/context.ts new file mode 100644 index 0000000000000..64029b649a58d --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/context.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 { createContext, useContext } from 'react'; +import { IDataStreamsStatsClient } from '../../services/data_streams_stats'; + +export interface DatasetQualityContextValue { + dataStreamsStatsServiceClient: IDataStreamsStatsClient; +} + +export const DatasetQualityContext = createContext({} as DatasetQualityContextValue); + +export function useDatasetQualityContext() { + return useContext(DatasetQualityContext); +} diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/dataset_quality.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/dataset_quality.tsx new file mode 100644 index 0000000000000..9fe6ca8db3b2f --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/dataset_quality.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { CoreStart } from '@kbn/core/public'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { DataStreamsStatsService } from '../../services/data_streams_stats/data_streams_stats_service'; +import { DatasetQualityContext, DatasetQualityContextValue } from './context'; +import { useKibanaContextForPluginProvider } from '../../utils'; +import { DatasetQualityStartDeps } from '../../types'; +import { Header } from './header'; +import { Table } from './table'; + +export interface CreateDatasetQualityArgs { + core: CoreStart; + plugins: DatasetQualityStartDeps; +} + +export const createDatasetQuality = ({ core, plugins }: CreateDatasetQualityArgs) => { + return () => { + const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins); + + const dataStreamsStatsServiceClient = new DataStreamsStatsService().start({ + http: core.http, + }).client; + + const datasetQualityProviderValue: DatasetQualityContextValue = { + dataStreamsStatsServiceClient, + }; + + return ( + + + + + + ); + }; +}; + +function DatasetQuality() { + return ( + + +
    + + + + + + ); +} diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx new file mode 100644 index 0000000000000..5126a645f7b6f --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiPageHeader, EuiButton } from '@elastic/eui'; +import { + ObservabilityOnboardingLocatorParams, + OBSERVABILITY_ONBOARDING_LOCATOR, +} from '@kbn/deeplinks-observability'; +import { datasetQualityAppTitle, onboardingLinkTitle } from '../../../common/translations'; +import { useKibanaContextForPlugin } from '../../utils'; + +export function Header() { + const { + services: { share }, + } = useKibanaContextForPlugin(); + + const OnboardingLink = React.memo(() => { + const locator = share.url.locators.get( + OBSERVABILITY_ONBOARDING_LOCATOR + ); + + const onboardingUrl = locator?.getRedirectUrl({}); + + return ( + + {onboardingLinkTitle} + + ); + }); + + return ( + ]} + /> + ); +} diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/index.ts b/x-pack/plugins/dataset_quality/public/components/dataset_quality/index.ts new file mode 100644 index 0000000000000..1a8591d0d3c86 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './dataset_quality'; diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/table.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/table.tsx new file mode 100644 index 0000000000000..f45df67170636 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/table.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiBasicTable, EuiHorizontalRule, EuiSpacer, EuiText, EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { loadingDatasetsText, noDatasetsTitle } from '../../../common/translations'; +import { useDatasetQualityTable } from '../../hooks'; + +export const Table = () => { + const { sort, onTableChange, pagination, renderedItems, columns, loading, resultsCount } = + useDatasetQualityTable(); + + return ( + <> + + + + + + {noDatasetsTitle}} + hasBorder={false} + titleSize="m" + /> + ) + } + /> + + ); +}; diff --git a/x-pack/plugins/dataset_quality/public/hooks/index.ts b/x-pack/plugins/dataset_quality/public/hooks/index.ts new file mode 100644 index 0000000000000..36b6f1540c828 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/hooks/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_dataset_quality_table'; diff --git a/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx b/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx new file mode 100644 index 0000000000000..30bbd7f437da8 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { orderBy } from 'lodash'; +import React, { useState, useMemo, useCallback } from 'react'; +import { useFetcher } from '@kbn/observability-shared-plugin/public'; +import { tableSummaryAllText, tableSummaryOfText } from '../../common/translations'; +import { DataStreamStat } from '../../common/data_streams_stats/data_stream_stat'; +import { getDatasetQualitTableColumns } from '../components/dataset_quality/columns'; +import { useDatasetQualityContext } from '../components/dataset_quality/context'; +import { useKibanaContextForPlugin } from '../utils'; + +const DEFAULT_SORT_FIELD = 'title'; +const DEFAULT_SORT_DIRECTION = 'desc'; +type DIRECTION = 'asc' | 'desc'; +type SORT_FIELD = keyof DataStreamStat; + +const sortingOverrides: Partial<{ [key in SORT_FIELD]: SORT_FIELD }> = { + ['size']: 'sizeBytes', +}; + +export const useDatasetQualityTable = () => { + const { + services: { fieldFormats }, + } = useKibanaContextForPlugin(); + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); + const [sortDirection, setSortDirection] = useState(DEFAULT_SORT_DIRECTION); + + const { dataStreamsStatsServiceClient: client } = useDatasetQualityContext(); + const { data = [], loading } = useFetcher(async () => client.getDataStreamsStats(), []); + + const columns = useMemo(() => getDatasetQualitTableColumns({ fieldFormats }), [fieldFormats]); + + const pagination = { + pageIndex, + pageSize, + totalItemCount: data.length, + hidePerPageOptions: true, + }; + + const onTableChange = useCallback( + (options: { + page: { index: number; size: number }; + sort?: { field: SORT_FIELD; direction: DIRECTION }; + }) => { + setPageIndex(options.page.index); + setPageSize(options.page.size); + setSortField(options.sort?.field || DEFAULT_SORT_FIELD); + setSortDirection(options.sort?.direction || DEFAULT_SORT_DIRECTION); + }, + [] + ); + + const sort = { + sort: { field: sortField, direction: sortDirection }, + }; + + const renderedItems = useMemo(() => { + const overridenSortingField = sortingOverrides[sortField] || sortField; + const sortedItems = orderBy(data, overridenSortingField, sortDirection); + + return sortedItems.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize); + }, [data, sortField, sortDirection, pageIndex, pageSize]); + + const resultsCount = useMemo(() => { + const startNumberItemsOnPage = pageSize * pageIndex + (renderedItems.length ? 1 : 0); + const endNumberItemsOnPage = pageSize * pageIndex + renderedItems.length; + + return pageSize === 0 ? ( + {tableSummaryAllText} + ) : ( + <> + + {startNumberItemsOnPage}-{endNumberItemsOnPage} + {' '} + {tableSummaryOfText} {data.length} + + ); + }, [data.length, pageIndex, pageSize, renderedItems.length]); + + return { sort, onTableChange, pagination, renderedItems, columns, loading, resultsCount }; +}; diff --git a/x-pack/plugins/dataset_quality/public/icons/logging.svg b/x-pack/plugins/dataset_quality/public/icons/logging.svg new file mode 100644 index 0000000000000..41d5251b3ea19 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/icons/logging.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/dataset_quality/public/index.ts b/x-pack/plugins/dataset_quality/public/index.ts index 339be1ec1de9b..e57d36776edfd 100644 --- a/x-pack/plugins/dataset_quality/public/index.ts +++ b/x-pack/plugins/dataset_quality/public/index.ts @@ -14,3 +14,5 @@ export type { DatasetQualityPluginSetup, DatasetQualityPluginStart } from './typ export function plugin(context: PluginInitializerContext) { return new DatasetQualityPlugin(context); } + +export { datasetQualityAppTitle } from '../common/translations'; diff --git a/x-pack/plugins/dataset_quality/public/plugin.ts b/x-pack/plugins/dataset_quality/public/plugin.tsx similarity index 60% rename from x-pack/plugins/dataset_quality/public/plugin.ts rename to x-pack/plugins/dataset_quality/public/plugin.tsx index 520c02481cd60..c2ab655422631 100644 --- a/x-pack/plugins/dataset_quality/public/plugin.ts +++ b/x-pack/plugins/dataset_quality/public/plugin.tsx @@ -6,11 +6,12 @@ */ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import { createDatasetQuality } from './components/dataset_quality'; import { DatasetQualityPluginSetup, DatasetQualityPluginStart, - DatasetQualitySetupDependencies, - DatasetQualityStartDependencies, + DatasetQualitySetupDeps, + DatasetQualityStartDeps, } from './types'; export class DatasetQualityPlugin @@ -18,14 +19,16 @@ export class DatasetQualityPlugin { constructor(context: PluginInitializerContext) {} - public setup(core: CoreSetup, plugins: DatasetQualitySetupDependencies) { + public setup(core: CoreSetup, plugins: DatasetQualitySetupDeps) { return {}; } - public start( - core: CoreStart, - plugins: DatasetQualityStartDependencies - ): DatasetQualityPluginStart { - return {}; + public start(core: CoreStart, plugins: DatasetQualityStartDeps): DatasetQualityPluginStart { + const DatasetQuality = createDatasetQuality({ + core, + plugins, + }); + + return { DatasetQuality }; } } diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts new file mode 100644 index 0000000000000..83028a3c4d660 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.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 { find, merge } from 'lodash'; +import { HttpStart } from '@kbn/core/public'; +import { DataStreamStat } from '../../../common/data_streams_stats/data_stream_stat'; +import { DATA_STREAMS_STATS_URL } from '../../../common/constants'; +import { + GetDataStreamsStatsError, + GetDataStreamsStatsResponse, + GetDataStreamsStatsQuery, + DataStreamStatServiceResponse, +} from '../../../common/data_streams_stats'; +import { IDataStreamsStatsClient } from './types'; + +export class DataStreamsStatsClient implements IDataStreamsStatsClient { + constructor(private readonly http: HttpStart) {} + + public async getDataStreamsStats( + params: GetDataStreamsStatsQuery = { type: 'logs' } + ): Promise { + const { dataStreamsStats, integrations } = await this.http + .get(DATA_STREAMS_STATS_URL, { + query: params, + }) + .catch((error) => { + throw new GetDataStreamsStatsError(`Failed to fetch data streams stats": ${error}`); + }); + + const mergedDataStreamsStats = dataStreamsStats.map((statsItem) => { + const integration = find(integrations, { name: statsItem.integration }); + + return integration ? merge({}, statsItem, { integration }) : statsItem; + }); + + return mergedDataStreamsStats.map(DataStreamStat.create); + } +} diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_service.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_service.ts new file mode 100644 index 0000000000000..c57fe3c90ebbf --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_service.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 { DataStreamsStatsClient } from './data_streams_stats_client'; +import { + DataStreamsStatsServiceSetup, + DataStreamsStatsServiceStartDeps, + DataStreamsStatsServiceStart, +} from './types'; + +export class DataStreamsStatsService { + constructor() {} + + public setup(): DataStreamsStatsServiceSetup {} + + public start({ http }: DataStreamsStatsServiceStartDeps): DataStreamsStatsServiceStart { + const client = new DataStreamsStatsClient(http); + + return { + client, + }; + } +} diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/index.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/index.ts new file mode 100644 index 0000000000000..8f33568e1e885 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './data_streams_stats_client'; +export * from './data_streams_stats_service'; +export * from './types'; diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts new file mode 100644 index 0000000000000..7023dbe254783 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpStart } from '@kbn/core/public'; +import { + DataStreamStatServiceResponse, + GetDataStreamsStatsQuery, +} from '../../../common/data_streams_stats'; + +export type DataStreamsStatsServiceSetup = void; + +export interface DataStreamsStatsServiceStart { + client: IDataStreamsStatsClient; +} + +export interface DataStreamsStatsServiceStartDeps { + http: HttpStart; +} + +export interface IDataStreamsStatsClient { + getDataStreamsStats(params?: GetDataStreamsStatsQuery): Promise; +} diff --git a/x-pack/plugins/dataset_quality/public/types.ts b/x-pack/plugins/dataset_quality/public/types.ts index 2d57bd6bb1b2e..482aff3b242b9 100644 --- a/x-pack/plugins/dataset_quality/public/types.ts +++ b/x-pack/plugins/dataset_quality/public/types.ts @@ -5,14 +5,22 @@ * 2.0. */ +import { ComponentType } from 'react'; +import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface DatasetQualityPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DatasetQualityPluginStart {} +export interface DatasetQualityPluginStart { + DatasetQuality: ComponentType; +} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DatasetQualityStartDependencies {} +export interface DatasetQualityStartDeps { + share: SharePluginStart; + fieldFormats: FieldFormatsStart; +} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DatasetQualitySetupDependencies {} +export interface DatasetQualitySetupDeps { + share: SharePluginSetup; +} diff --git a/x-pack/plugins/dataset_quality/public/utils/index.ts b/x-pack/plugins/dataset_quality/public/utils/index.ts new file mode 100644 index 0000000000000..c7d8b7ba1dcab --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/utils/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_kibana'; diff --git a/x-pack/plugins/dataset_quality/public/utils/use_kibana.tsx b/x-pack/plugins/dataset_quality/public/utils/use_kibana.tsx new file mode 100644 index 0000000000000..cd13ced6af9b7 --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/utils/use_kibana.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import { + createKibanaReactContext, + KibanaReactContextValue, + useKibana, +} from '@kbn/kibana-react-plugin/public'; +import { useMemo } from 'react'; +import { DatasetQualityStartDeps } from '../types'; + +export type PluginKibanaContextValue = CoreStart & DatasetQualityStartDeps; + +export const createKibanaContextForPlugin = (core: CoreStart, plugins: DatasetQualityStartDeps) => + createKibanaReactContext({ + ...core, + ...plugins, + }); + +export const useKibanaContextForPlugin = + useKibana as () => KibanaReactContextValue; + +export const useKibanaContextForPluginProvider = ( + core: CoreStart, + plugins: DatasetQualityStartDeps +) => { + const { Provider } = useMemo(() => createKibanaContextForPlugin(core, plugins), [core, plugins]); + + return Provider; +}; diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/get_data_streams.test.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/get_data_streams.test.ts index ef1ecdbccb1d1..0b45d6fa8b34d 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/get_data_streams.test.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/get_data_streams.test.ts @@ -114,7 +114,6 @@ describe('getDataStreams', () => { esClient: esClientMock, type: 'logs', datasetQuery: 'nginx', - sortOrder: 'asc', uncategorisedOnly: true, }); expect(dataStreamService.getMatchingDataStreams).toHaveBeenCalledWith(expect.anything(), { @@ -129,7 +128,6 @@ describe('getDataStreams', () => { esClient: esClientMock, type: 'logs', datasetQuery: 'nginx', - sortOrder: 'asc', uncategorisedOnly: true, }); expect(results.items.length).toBe(1); @@ -140,62 +138,36 @@ describe('getDataStreams', () => { esClient: esClientMock, type: 'logs', datasetQuery: 'nginx', - sortOrder: 'asc', uncategorisedOnly: false, }); expect(results.items.length).toBe(5); }); }); - describe('Can be sorted', () => { - it('Ascending', async () => { - const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - const results = await getDataStreams({ - esClient: esClientMock, - type: 'logs', - datasetQuery: 'nginx', - sortOrder: 'asc', - uncategorisedOnly: false, - }); - expect(results.items[0].name).toBe('logs-elastic_agent-default'); - }); - it('Descending', async () => { - const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - const results = await getDataStreams({ - esClient: esClientMock, - type: 'logs', - datasetQuery: 'nginx', - sortOrder: 'desc', - uncategorisedOnly: false, - }); - expect(results.items[0].name).toBe('logs-test.test-default'); - }); - }); it('Formats the items correctly', async () => { const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); const results = await getDataStreams({ esClient: esClientMock, type: 'logs', - sortOrder: 'desc', uncategorisedOnly: false, }); - expect(results.items).toEqual([ - { name: 'logs-test.test-default' }, + expect(results.items.sort()).toEqual([ { - name: 'logs-elastic_agent.metricbeat-default', - integration: { name: 'elastic_agent', managed_by: 'fleet' }, + name: 'logs-elastic_agent-default', + integration: 'elastic_agent', }, { - name: 'logs-elastic_agent.fleet_server-default', - integration: { name: 'elastic_agent', managed_by: 'fleet' }, + name: 'logs-elastic_agent.filebeat-default', + integration: 'elastic_agent', }, { - name: 'logs-elastic_agent.filebeat-default', - integration: { name: 'elastic_agent', managed_by: 'fleet' }, + name: 'logs-elastic_agent.fleet_server-default', + integration: 'elastic_agent', }, { - name: 'logs-elastic_agent-default', - integration: { name: 'elastic_agent', managed_by: 'fleet' }, + name: 'logs-elastic_agent.metricbeat-default', + integration: 'elastic_agent', }, + { name: 'logs-test.test-default' }, ]); }); }); diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts index 37181d8b8a731..6154e3bc11a24 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts @@ -13,10 +13,9 @@ export async function getDataStreams(options: { esClient: ElasticsearchClient; type?: DataStreamTypes; datasetQuery?: string; - sortOrder: 'asc' | 'desc'; uncategorisedOnly: boolean; }) { - const { esClient, type, datasetQuery, uncategorisedOnly, sortOrder } = options; + const { esClient, type, datasetQuery, uncategorisedOnly } = options; const allDataStreams = await dataStreamService.getMatchingDataStreams(esClient, { type: type ?? '*', @@ -31,25 +30,10 @@ export async function getDataStreams(options: { const mappedDataStreams = filteredDataStreams.map((dataStream) => ({ name: dataStream.name, - ...(dataStream._meta - ? { - integration: { - name: dataStream._meta?.package?.name, - managed_by: dataStream._meta?.managed_by, - }, - } - : {}), + integration: dataStream._meta?.package?.name, })); - const sortedDataStreams = mappedDataStreams.sort((a, b) => { - if (sortOrder === 'desc') { - return b.name.localeCompare(a.name); - } - - return a.name.localeCompare(b.name); - }); - return { - items: sortedDataStreams, + items: mappedDataStreams, }; } diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts index c078cb24d3d28..830c3b162573f 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts @@ -63,52 +63,30 @@ describe('getDataStreams', () => { esClient: esClientMock, type: 'logs', datasetQuery: 'nginx', - sortOrder: 'asc', }); expect(dataStreamService.getMatchingDataStreamsStats).toHaveBeenCalledWith(expect.anything(), { type: 'logs', dataset: '*nginx*', }); }); - describe('Can be sorted', () => { - it('Ascending', async () => { - const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - const results = await getDataStreamsStats({ - esClient: esClientMock, - type: 'logs', - sortOrder: 'asc', - }); - expect(results.items[0].name).toBe('logs-elastic_agent-default'); - }); - it('Descending', async () => { - const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - const results = await getDataStreamsStats({ - esClient: esClientMock, - type: 'logs', - sortOrder: 'desc', - }); - expect(results.items[0].name).toBe('logs-test.test-default'); - }); - }); it('Formats the items correctly', async () => { const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); const results = await getDataStreamsStats({ esClient: esClientMock, type: 'logs', - sortOrder: 'desc', }); - expect(results.items).toEqual([ + expect(results.items.sort()).toEqual([ { - name: 'logs-test.test-default', - size: '6.2mb', - size_bytes: 6570447, - last_activity: 1698913802000, + name: 'logs-elastic_agent-default', + size: '1gb', + size_bytes: 1170805528, + last_activity: 1698916071000, }, { - name: 'logs-elastic_agent.metricbeat-default', - size: '1.6mb', - size_bytes: 1704807, - last_activity: 1698672046707, + name: 'logs-elastic_agent.filebeat-default', + size: '1.3mb', + size_bytes: 1459100, + last_activity: 1698902209996, }, { name: 'logs-elastic_agent.fleet_server-default', @@ -117,16 +95,16 @@ describe('getDataStreams', () => { last_activity: 1698914110010, }, { - name: 'logs-elastic_agent.filebeat-default', - size: '1.3mb', - size_bytes: 1459100, - last_activity: 1698902209996, + name: 'logs-elastic_agent.metricbeat-default', + size: '1.6mb', + size_bytes: 1704807, + last_activity: 1698672046707, }, { - name: 'logs-elastic_agent-default', - size: '1gb', - size_bytes: 1170805528, - last_activity: 1698916071000, + name: 'logs-test.test-default', + size: '6.2mb', + size_bytes: 6570447, + last_activity: 1698913802000, }, ]); }); diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts index 2f0f8057cddef..9ec252d096357 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts @@ -13,9 +13,8 @@ export async function getDataStreamsStats(options: { esClient: ElasticsearchClient; type?: DataStreamTypes; datasetQuery?: string; - sortOrder: 'asc' | 'desc'; }) { - const { esClient, type, datasetQuery, sortOrder } = options; + const { esClient, type, datasetQuery } = options; const matchingDataStreamsStats = await dataStreamService.getMatchingDataStreamsStats(esClient, { type: type ?? '*', @@ -31,15 +30,7 @@ export async function getDataStreamsStats(options: { }; }); - const sortedDataStreams = mappedDataStreams.sort((a, b) => { - if (sortOrder === 'desc') { - return b.name.localeCompare(a.name); - } - - return a.name.localeCompare(b.name); - }); - return { - items: sortedDataStreams, + items: mappedDataStreams, }; } diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts index f973211cc7341..4217b9711226e 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { keyBy, merge, values } from 'lodash'; -import { dataStreamTypesRt, sortOrderRt } from '../../types/api_types'; +import { dataStreamTypesRt } from '../../../common/data_streams'; import { DataStreamsStatResponse } from '../../types/data_stream'; import { createDatasetQualityServerRoute } from '../create_datasets_quality_server_route'; import { getDataStreams } from './get_data_streams'; @@ -21,19 +21,22 @@ const statsRoute = createDatasetQualityServerRoute({ t.partial({ datasetQuery: t.string, }), - sortOrderRt, ]), }), options: { tags: [], }, async handler(resources): Promise { - const { context, params } = resources; + const { context, params, plugins } = resources; const coreContext = await context.core; // Query datastreams as the current user as the Kibana internal user may not have all the required permissions const esClient = coreContext.elasticsearch.client.asCurrentUser; + const fleetPluginStart = await plugins.fleet.start(); + const packageClient = fleetPluginStart.packageService.asInternalUser; + const packages = await packageClient.getPackages(); + const [dataStreams, dataStreamsStats] = await Promise.all([ getDataStreams({ esClient, @@ -43,8 +46,22 @@ const statsRoute = createDatasetQualityServerRoute({ getDataStreamsStats({ esClient, ...params.query }), ]); + const installedPackages = dataStreams.items.map((item) => item.integration); + + const integrations = packages + .filter((pkg) => installedPackages.includes(pkg.name)) + .map((p) => ({ + name: p.name, + title: p.title, + version: p.version, + icons: p.icons, + })); + return { - items: values(merge(keyBy(dataStreams.items, 'name'), keyBy(dataStreamsStats.items, 'name'))), + dataStreamsStats: values( + merge(keyBy(dataStreams.items, 'name'), keyBy(dataStreamsStats.items, 'name')) + ), + integrations, }; }, }); diff --git a/x-pack/plugins/dataset_quality/server/types.ts b/x-pack/plugins/dataset_quality/server/types.ts index f9a184cfb768c..3874040f5d3bf 100644 --- a/x-pack/plugins/dataset_quality/server/types.ts +++ b/x-pack/plugins/dataset_quality/server/types.ts @@ -6,12 +6,15 @@ */ import { CustomRequestHandlerContext } from '@kbn/core/server'; +import { FleetSetupContract, FleetStartContract } from '@kbn/fleet-plugin/server'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DatasetQualityPluginSetupDependencies {} +export interface DatasetQualityPluginSetupDependencies { + fleet: FleetSetupContract; +} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DatasetQualityPluginStartDependencies {} +export interface DatasetQualityPluginStartDependencies { + fleet: FleetStartContract; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface DatasetQualityPluginSetup {} diff --git a/x-pack/plugins/dataset_quality/server/types/data_stream.ts b/x-pack/plugins/dataset_quality/server/types/data_stream.ts index 582746755ac51..423d85985c599 100644 --- a/x-pack/plugins/dataset_quality/server/types/data_stream.ts +++ b/x-pack/plugins/dataset_quality/server/types/data_stream.ts @@ -6,9 +6,11 @@ */ import { ByteSize } from '@elastic/elasticsearch/lib/api/types'; +import { Integration } from './integration'; export interface DataStreamsStatResponse { - items: DataStreamStat[]; + dataStreamsStats: DataStreamStat[]; + integrations: Integration[]; } export interface DataStreamStat { @@ -16,10 +18,7 @@ export interface DataStreamStat { size?: ByteSize; size_bytes?: number; last_activity?: number; - integration?: { - name?: string; - managed_by?: string; - }; + integration?: Integration; } export type DataStreamTypes = 'logs' | 'metrics' | 'traces' | 'synthetics' | 'profiling'; diff --git a/x-pack/plugins/dataset_quality/server/types/integration.ts b/x-pack/plugins/dataset_quality/server/types/integration.ts new file mode 100644 index 0000000000000..2595a120c8b70 --- /dev/null +++ b/x-pack/plugins/dataset_quality/server/types/integration.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. + */ + +export interface Integration { + name: string; + title?: string; + version?: string; + icons?: IntegrationIcon[]; +} + +export interface IntegrationIcon { + path: string; + src: string; + title?: string; + size?: string; + type?: string; +} diff --git a/x-pack/plugins/dataset_quality/tsconfig.json b/x-pack/plugins/dataset_quality/tsconfig.json index 8e3ebcdb7e741..32baf4671143e 100644 --- a/x-pack/plugins/dataset_quality/tsconfig.json +++ b/x-pack/plugins/dataset_quality/tsconfig.json @@ -11,10 +11,19 @@ ], "kbn_references": [ "@kbn/core", - "@kbn/server-route-repository", "@kbn/core-plugins-server", "@kbn/core-elasticsearch-server-mocks", + "@kbn/deeplinks-observability", + "@kbn/fleet-plugin", + "@kbn/observability-shared-plugin", + "@kbn/server-route-repository", + "@kbn/share-plugin", "@kbn/std", + "@kbn/i18n", + "@kbn/kibana-react-plugin", + "@kbn/i18n-react", + "@kbn/field-formats-plugin", + "@kbn/field-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts index 7f06b4b456194..7db042a9c9cf0 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.test.ts @@ -97,9 +97,13 @@ describe('callAgentExecutor', () => { kbResource: ESQL_RESOURCE, }); - expect(mockCall).toHaveBeenCalledWith({ - input: '\n\nDo you know my name?', - }); + // We don't care about the `config` argument, so we use `expect.anything()` + expect(mockCall).toHaveBeenCalledWith( + { + input: '\n\nDo you know my name?', + }, + expect.anything() + ); }); it('kicks off the chain with the expected message when langChainMessages has only one entry', async () => { @@ -115,9 +119,13 @@ describe('callAgentExecutor', () => { kbResource: ESQL_RESOURCE, }); - expect(mockCall).toHaveBeenCalledWith({ - input: 'What is my name?', - }); + // We don't care about the `config` argument, so we use `expect.anything()` + expect(mockCall).toHaveBeenCalledWith( + { + input: 'What is my name?', + }, + expect.anything() + ); }); it('returns the expected response body', async () => { diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts index ad54b0d1e8b40..3a27226dc804d 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts @@ -14,7 +14,16 @@ import { ElasticsearchStore } from '../elasticsearch_store/elasticsearch_store'; import { ActionsClientLlm } from '../llm/actions_client_llm'; import { KNOWLEDGE_BASE_INDEX_PATTERN } from '../../../routes/knowledge_base/constants'; import type { AgentExecutorParams, AgentExecutorResponse } from '../executors/types'; +import { withAssistantSpan } from '../tracers/with_assistant_span'; +import { APMTracer } from '../tracers/apm_tracer'; +export const DEFAULT_AGENT_EXECUTOR_ID = 'Elastic AI Assistant Agent Executor'; + +/** + * The default agent executor used by the Elastic AI Assistant. Main agent/chain that wraps the ActionsClientLlm, + * sets up a conversation BufferMemory from chat history, and registers tools like the ESQLKnowledgeBaseTool. + * + */ export const callAgentExecutor = async ({ actions, connectorId, @@ -25,6 +34,7 @@ export const callAgentExecutor = async ({ request, elserId, kbResource, + traceOptions, }: AgentExecutorParams): AgentExecutorResponse => { const llm = new ActionsClientLlm({ actions, connectorId, request, llmType, logger }); @@ -58,12 +68,14 @@ export const callAgentExecutor = async ({ // Create a chain that uses the ELSER backed ElasticsearchStore, override k=10 for esql query generation for now const chain = RetrievalQAChain.fromLLM(llm, esStore.asRetriever(10)); + // TODO: Dependency inject these tools const tools: Tool[] = [ new ChainTool({ - name: 'esql-language-knowledge-base', + name: 'ESQLKnowledgeBaseTool', description: 'Call this for knowledge on how to build an ESQL query, or answer questions about the ES|QL query language.', chain, + tags: ['esql', 'query-generation', 'knowledge-base'], }), ]; @@ -73,11 +85,37 @@ export const callAgentExecutor = async ({ verbose: false, }); - await executor.call({ input: latestMessage[0].content }); + // Sets up tracer for tracing executions to APM. See x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/README.mdx + // If LangSmith env vars are set, executions will be traced there as well. See https://docs.smith.langchain.com/tracing + const apmTracer = new APMTracer({ projectName: traceOptions?.projectName ?? 'default' }, logger); + + let traceData; + + // Wrap executor call with an APM span for instrumentation + await withAssistantSpan(DEFAULT_AGENT_EXECUTOR_ID, async (span) => { + if (span?.transaction?.ids['transaction.id'] != null && span?.ids['trace.id'] != null) { + traceData = { + // Transactions ID since this span is the parent + transaction_id: span.transaction.ids['transaction.id'], + trace_id: span.ids['trace.id'], + }; + span.addLabels({ evaluationId: traceOptions?.evaluationId }); + } + + return executor.call( + { input: latestMessage[0].content }, + { + callbacks: [apmTracer, ...(traceOptions?.tracers ?? [])], + runName: DEFAULT_AGENT_EXECUTOR_ID, + tags: traceOptions?.tags ?? [], + } + ); + }); return { connector_id: connectorId, data: llm.getActionResultData(), // the response from the actions framework + trace_data: traceData, status: 'ok', }; }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/openai_functions_executor.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/openai_functions_executor.ts index 608b15eed384f..32c9c0988f4d4 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/openai_functions_executor.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/openai_functions_executor.ts @@ -14,6 +14,11 @@ import { ElasticsearchStore } from '../elasticsearch_store/elasticsearch_store'; import { ActionsClientLlm } from '../llm/actions_client_llm'; import { KNOWLEDGE_BASE_INDEX_PATTERN } from '../../../routes/knowledge_base/constants'; import type { AgentExecutorParams, AgentExecutorResponse } from './types'; +import { withAssistantSpan } from '../tracers/with_assistant_span'; +import { APMTracer } from '../tracers/apm_tracer'; + +export const OPEN_AI_FUNCTIONS_AGENT_EXECUTOR_ID = + 'Elastic AI Assistant Agent Executor (OpenAI Functions)'; /** * This is an agent executor to be used with the model evaluation API for benchmarking. @@ -25,11 +30,13 @@ export const callOpenAIFunctionsExecutor = async ({ actions, connectorId, esClient, - elserId, langChainMessages, llmType, logger, request, + elserId, + kbResource, + traceOptions, }: AgentExecutorParams): AgentExecutorResponse => { const llm = new ActionsClientLlm({ actions, connectorId, request, llmType, logger }); @@ -45,15 +52,32 @@ export const callOpenAIFunctionsExecutor = async ({ }); // ELSER backed ElasticsearchStore for Knowledge Base - const esStore = new ElasticsearchStore(esClient, KNOWLEDGE_BASE_INDEX_PATTERN, logger, elserId); - const chain = RetrievalQAChain.fromLLM(llm, esStore.asRetriever()); + const esStore = new ElasticsearchStore( + esClient, + KNOWLEDGE_BASE_INDEX_PATTERN, + logger, + elserId, + kbResource + ); + + const modelExists = await esStore.isModelInstalled(); + if (!modelExists) { + throw new Error( + 'Please ensure ELSER is configured to use the Knowledge Base, otherwise disable the Knowledge Base in Advanced Settings to continue.' + ); + } + + // Create a chain that uses the ELSER backed ElasticsearchStore, override k=10 for esql query generation for now + const chain = RetrievalQAChain.fromLLM(llm, esStore.asRetriever(10)); + // TODO: Dependency inject these tools const tools: Tool[] = [ new ChainTool({ - name: 'esql-language-knowledge-base', + name: 'ESQLKnowledgeBaseTool', description: 'Call this for knowledge on how to build an ESQL query, or answer questions about the ES|QL query language.', chain, + tags: ['esql', 'query-generation', 'knowledge-base'], }), ]; @@ -63,11 +87,37 @@ export const callOpenAIFunctionsExecutor = async ({ verbose: false, }); - await executor.call({ input: latestMessage[0].content }); + // Sets up tracer for tracing executions to APM. See x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/README.mdx + // If LangSmith env vars are set, executions will be traced there as well. See https://docs.smith.langchain.com/tracing + const apmTracer = new APMTracer({ projectName: traceOptions?.projectName ?? 'default' }, logger); + + let traceData; + + // Wrap executor call with an APM span for instrumentation + await withAssistantSpan(OPEN_AI_FUNCTIONS_AGENT_EXECUTOR_ID, async (span) => { + if (span?.transaction?.ids['transaction.id'] != null && span?.ids['trace.id'] != null) { + traceData = { + // Transactions ID since this span is the parent + transaction_id: span.transaction.ids['transaction.id'], + trace_id: span.ids['trace.id'], + }; + span.addLabels({ evaluationId: traceOptions?.evaluationId }); + } + + return executor.call( + { input: latestMessage[0].content }, + { + callbacks: [apmTracer, ...(traceOptions?.tracers ?? [])], + runName: OPEN_AI_FUNCTIONS_AGENT_EXECUTOR_ID, + tags: traceOptions?.tags ?? [], + } + ); + }); return { connector_id: connectorId, data: llm.getActionResultData(), // the response from the actions framework + trace_data: traceData, status: 'ok', }; }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index 1c15ff8c97da4..e4d564f677e14 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -10,6 +10,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { BaseMessage } from 'langchain/schema'; import { Logger } from '@kbn/logging'; import { KibanaRequest } from '@kbn/core-http-server'; +import type { LangChainTracer } from 'langchain/callbacks'; import { RequestBody, ResponseBody } from '../types'; export interface AgentExecutorParams { @@ -22,10 +23,31 @@ export interface AgentExecutorParams { logger: Logger; request: KibanaRequest; elserId?: string; + traceOptions?: TraceOptions; } export type AgentExecutorResponse = Promise; export type AgentExecutor = (params: AgentExecutorParams) => AgentExecutorResponse; -export type AgentExecutorEvaluator = (langChainMessages: BaseMessage[]) => AgentExecutorResponse; +export type AgentExecutorEvaluator = ( + langChainMessages: BaseMessage[], + exampleId?: string +) => AgentExecutorResponse; + +export interface AgentExecutorEvaluatorWithMetadata { + agentEvaluator: AgentExecutorEvaluator; + metadata: { + connectorName: string; + runName: string; + }; +} + +export interface TraceOptions { + evaluationId?: string; + exampleId?: string; + projectName?: string; + runName?: string; + tags?: string[]; + tracers?: LangChainTracer[]; +} diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts index 99fa1ac946909..ca1afdeef7368 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/llm/actions_client_llm.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { v4 as uuidv4 } from 'uuid'; import { KibanaRequest, Logger } from '@kbn/core/server'; import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; import { LLM } from 'langchain/llms/base'; @@ -15,12 +16,22 @@ import { RequestBody } from '../types'; const LLM_TYPE = 'ActionsClientLlm'; +interface ActionsClientLlmParams { + actions: ActionsPluginStart; + connectorId: string; + llmType?: string; + logger: Logger; + request: KibanaRequest; + traceId?: string; +} + export class ActionsClientLlm extends LLM { #actions: ActionsPluginStart; #connectorId: string; #logger: Logger; #request: KibanaRequest; #actionResultData: string; + #traceId: string; // Local `llmType` as it can change and needs to be accessed by abstract `_llmType()` method // Not using getter as `this._llmType()` is called in the constructor via `super({})` @@ -29,20 +40,16 @@ export class ActionsClientLlm extends LLM { constructor({ actions, connectorId, + traceId = uuidv4(), llmType, logger, request, - }: { - actions: ActionsPluginStart; - connectorId: string; - llmType?: string; - logger: Logger; - request: KibanaRequest; - }) { + }: ActionsClientLlmParams) { super({}); this.#actions = actions; this.#connectorId = connectorId; + this.#traceId = traceId; this.llmType = llmType ?? LLM_TYPE; this.#logger = logger; this.#request = request; @@ -68,7 +75,9 @@ export class ActionsClientLlm extends LLM { // convert the Langchain prompt to an assistant message: const assistantMessage = getMessageContentAndRole(prompt); this.#logger.debug( - `ActionsClientLlm#_call assistantMessage:\n${JSON.stringify(assistantMessage)} ` + `ActionsClientLlm#_call\ntraceId: ${this.#traceId}\nassistantMessage:\n${JSON.stringify( + assistantMessage + )} ` ); // create a new connector request body with the assistant message: const requestBody = { diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/README.mdx b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/README.mdx new file mode 100644 index 0000000000000..9e5bec7ea3371 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/README.mdx @@ -0,0 +1,68 @@ +### Tracing LangChain Retrievers, LLMs, Chains, and Tools using Elastic APM and LangSmith + +This document describes how to trace LangChain retrievers, LLMs, chains, and tools using Elastic APM and LangSmith. + +If the `assistantModelEvaluation` experimental feature flag is enabled, and an APM server is configured, messages that have a corresponding trace will have an additional `View APM trace` action in the message title bar: + +

    + +

    + +Viewing the trace you can see a breakdown of the time spent in each retriever, llm, chain, and tool: +

    + +

    + +The Evaluation interface has been updated to support adding additional metadata like `Project Name`, `Run Name`, and pulling test datasets from LangSmith. Predictions can now also be run without having to run an Evaluation, so datasets can quickly be run for manual analysis. + +

    + +

    + + +

    + +

    + + +### Configuring APM + +First, enable the `assistantModelEvaluation` experimental feature flag by adding the following to your `kibana.dev.yml`: + +``` +xpack.securitySolution.enableExperimental: [ 'assistantModelEvaluation' ] +``` + +Next, you'll need an APM server to collect the traces. You can either [follow the documentation for installing](https://www.elastic.co/guide/en/apm/guide/current/installing.html) the released artifact, or [run from source](https://github.com/elastic/apm-server#apm-server-development) and set up using the [quickstart guide provided](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) (be sure to install the APM Server integration to ensure the necessary indices are created!). Once your APM server is running, add your APM server configuration to your `kibana.dev.yml` as well using the following: + +``` +# APM +elastic.apm: + active: true + environment: 'SpongBox5002c™' + serverUrl: 'http://localhost:8200' + transactionSampleRate: 1.0 + breakdownMetrics: true + spanStackTraceMinDuration: 10ms + # Disables Kibana RUM + servicesOverrides.kibana-frontend.active: false +``` + +> [!NOTE] +> If connecting to a cloud APM server (like our [ai-assistant apm deployment](https://ai-assistant-apm-do-not-delete.kb.us-central1.gcp.cloud.es.io/)), follow [these steps](https://www.elastic.co/guide/en/apm/guide/current/api-key.html#create-an-api-key) to create an API key, and then set it via `apiKey` and also set your `serverUrl` as shown in the APM Integration details within fleet. Note that the `View APM trace` button within the UI will link to your local instance, not the cloud instance. + +> [!NOTE] +> If you're an Elastic developer running Kibana from source, you can just enable APM as above, and _not_ include a `serverUrl`, and your traces will be sent to the https://kibana-cloud-apm.elastic.dev cluster. Note that the `View APM trace` button within the UI will link to your local instance, not the cloud instance. + +### Configuring LangSmith + +If wanting to push traces to LangSmith, or leverage any datasets that you may have hosted in a project, all you need to do is configure a few environment variables, and then start the kibana server. See the [LangSmith Traces documentation](https://docs.smith.langchain.com/tracing) for details, or just add the below env variables to enable: + +``` +# LangChain LangSmith +export LANGCHAIN_TRACING_V2=true +export LANGCHAIN_ENDPOINT="https://api.smith.langchain.com" +export LANGCHAIN_API_KEY="" +export LANGCHAIN_PROJECT="8.12 ESQL Query Generation" +``` + diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/apm_tracer.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/apm_tracer.ts new file mode 100644 index 0000000000000..6aa00effc16f9 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/apm_tracer.ts @@ -0,0 +1,148 @@ +/* + * Copyright 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 { BaseCallbackHandlerInput, BaseTracer, Run } from 'langchain/callbacks'; +import agent from 'elastic-apm-node'; +import type { Logger } from '@kbn/core/server'; + +export interface LangChainTracerFields extends BaseCallbackHandlerInput { + exampleId?: string; + projectName?: string; +} + +type Span = Exclude; + +/** + * APMTracer is a tracer that uses the Elastic APM agent to trace langchain retrievers, llms, chains, and tools. + */ +export class APMTracer extends BaseTracer implements LangChainTracerFields { + name = 'apm_tracer'; + projectName?: string; + exampleId?: string; + logger: Logger; + + retrieverSpans: Span[] = []; + llmSpans: Span[] = []; + chainSpans: Span[] = []; + toolSpans: Span[] = []; + + constructor(fields: LangChainTracerFields = {}, logger: Logger) { + super(fields); + const { exampleId, projectName } = fields; + + this.projectName = projectName ?? 'default'; + this.exampleId = exampleId; + this.logger = logger; + } + + protected async persistRun(_run: Run): Promise {} + + /** + * LangChain Run's contain a lot of useful information, so here we unpack as much of it as we can + * into labels that can be added to the corresponding span. Stringifying outputs at the moment since + * the Run schema is a loose KVMap, but we should more elegantly unpack relevant data that we find useful + * + * See BaseRun interface Run extends from + * + * @param run + * @protected + */ + protected _getLabelsFromRun(run: Run): agent.Labels { + try { + return { + tags: JSON.stringify(run.tags), + outputs: JSON.stringify(run.outputs), + events: JSON.stringify(run.events), + inputs: JSON.stringify(run.inputs), + }; + } catch (e) { + this.logger.error(`Error parsing run into labels:\n${e}`); + return {}; + } + } + + protected createAndAddSpanFromRun(run: Run, spans: Span[]) { + const span = agent.startSpan(run.name) ?? undefined; + + if (span) { + span.addLabels(this._getLabelsFromRun(run)); + spans.push(span); + } + } + + async onRetrieverStart(run: Run): Promise { + this.logger.debug(`onRetrieverStart: run:\n${JSON.stringify(run, null, 2)}`); + this.createAndAddSpanFromRun(run, this.retrieverSpans); + } + + async onRetrieverEnd(run: Run): Promise { + this.logger.debug(`onRetrieverEnd: run:\n${JSON.stringify(run, null, 2)}`); + const span = this.retrieverSpans.pop(); + if (span != null) { + span.addLabels(this._getLabelsFromRun(run)); + span.end(); + } + } + + async onRetrieverError(run: Run): Promise { + this.logger.debug(`onRetrieverError: run:\n${JSON.stringify(run, null, 2)}`); + } + + async onLLMStart(run: Run): Promise { + this.logger.debug(`onLLMStart: run:\n${JSON.stringify(run, null, 2)}`); + this.createAndAddSpanFromRun(run, this.llmSpans); + } + + async onLLMEnd(run: Run): Promise { + this.logger.debug(`onLLMEnd: run:\n${JSON.stringify(run, null, 2)}`); + const span = this.llmSpans.pop(); + if (span != null) { + span.addLabels(this._getLabelsFromRun(run)); + span.end(); + } + } + + async onLLMError(run: Run): Promise { + this.logger.debug(`onLLMError: run:\n${JSON.stringify(run, null, 2)}`); + } + + async onChainStart(run: Run): Promise { + this.logger.debug(`onChainStart: run:\n${JSON.stringify(run, null, 2)}`); + this.createAndAddSpanFromRun(run, this.chainSpans); + } + + async onChainEnd(run: Run): Promise { + this.logger.debug(`onChainEnd: run:\n${JSON.stringify(run, null, 2)}`); + const span = this.chainSpans.pop(); + if (span != null) { + span.addLabels(this._getLabelsFromRun(run)); + span.end(); + } + } + + async onChainError(run: Run): Promise { + this.logger.debug(`onChainError: run:\n${JSON.stringify(run, null, 2)}`); + } + + async onToolStart(run: Run): Promise { + this.logger.debug(`onToolStart: run:\n${JSON.stringify(run, null, 2)}`); + this.createAndAddSpanFromRun(run, this.toolSpans); + } + + async onToolEnd(run: Run): Promise { + this.logger.debug(`onToolEnd: run:\n${JSON.stringify(run, null, 2)}`); + const span = this.toolSpans.pop(); + if (span != null) { + span.addLabels(this._getLabelsFromRun(run)); + span.end(); + } + } + + async onToolError(run: Run): Promise { + this.logger.debug(`onToolError: run:\n${JSON.stringify(run, null, 2)}`); + } +} diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/with_assistant_span.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/with_assistant_span.ts new file mode 100644 index 0000000000000..8f9ef99c255a7 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/tracers/with_assistant_span.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 type { SpanOptions } from '@kbn/apm-utils'; +import { withSpan } from '@kbn/apm-utils'; +import type agent from 'elastic-apm-node'; + +type Span = Exclude; + +/** + * This is a thin wrapper around withSpan from @kbn/apm-utils, which sets + * span type to 'elasticAssistant' by default. This span type is used to + * distinguish assistant spans from everything else when inspecting traces. + * + * Use this method to capture information about the execution of a specific + * code path and highlight it in APM UI. + * + * @param optionsOrName Span name or span options object + * @param cb Code block you want to measure + * + * @returns Whatever the measured code block returns + */ +export const withAssistantSpan = ( + optionsOrName: SpanOptions | string, + cb: (span?: Span) => Promise +) => + withSpan( + { + type: 'elasticAssistant', + ...(typeof optionsOrName === 'string' ? { name: optionsOrName } : optionsOrName), + }, + cb + ); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts index 620f2554ecf43..03f106b4b8796 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/types.ts @@ -13,4 +13,8 @@ export interface ResponseBody { status: string; data: string; connector_id: string; + trace_data?: { + transaction_id: string; + trace_id: string; + }; } diff --git a/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/evaluation.ts b/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/evaluation.ts index e4c4e45c01400..54040d3d1b58e 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/evaluation.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/evaluation.ts @@ -8,34 +8,40 @@ import { loadEvaluator } from 'langchain/evaluation'; import { LLM } from 'langchain/llms/base'; import { ChainValues, HumanMessage } from 'langchain/schema'; -import { chunk } from 'lodash/fp'; +import { chunk as createChunks } from 'lodash/fp'; import { Logger } from '@kbn/core/server'; import { ToolingLog } from '@kbn/tooling-log'; -import { asyncForEach } from '@kbn/std'; -import { AgentExecutorEvaluator } from '../langchain/executors/types'; +import { LangChainTracer, RunCollectorCallbackHandler } from 'langchain/callbacks'; +import { AgentExecutorEvaluatorWithMetadata } from '../langchain/executors/types'; import { Dataset } from '../../schemas/evaluate/post_evaluate'; -import { callAgentWithRetry, getMessageFromLangChainResponse, wait } from './utils'; +import { callAgentWithRetry, getMessageFromLangChainResponse } from './utils'; import { ResponseBody } from '../langchain/types'; +import { isLangSmithEnabled, writeLangSmithFeedback } from '../../routes/evaluate/utils'; export interface PerformEvaluationParams { - agentExecutorEvaluators: AgentExecutorEvaluator[]; + agentExecutorEvaluators: AgentExecutorEvaluatorWithMetadata[]; dataset: Dataset; evaluationId: string; - evaluatorModel: LLM; + evaluatorModel?: LLM; evaluationPrompt?: string; - evaluationType: string; - maxConcurrency?: number; + evaluationType?: string; logger: Logger | ToolingLog; + maxConcurrency?: number; + runName?: string; } export interface EvaluationResult { '@timestamp': string; + connectorName: string; evaluation: ChainValues; evaluationId: string; input: string; + inputExampleId?: string | undefined; + langSmithLink?: string | undefined; prediction: string; predictionResponse: PromiseSettledResult; reference: string; + runName: string; } export interface EvaluationSummary { @@ -44,6 +50,8 @@ export interface EvaluationSummary { evaluationEnd: number; evaluationId: string; evaluationDuration: number; + langSmithLink?: string | undefined; + runName: string; totalAgents: number; totalRequests: number; totalInput: number; @@ -61,45 +69,85 @@ export const performEvaluation = async ({ evaluatorModel, evaluationPrompt, evaluationType, - maxConcurrency = 3, + maxConcurrency = 1, logger, + runName = 'default-run-name', }: PerformEvaluationParams) => { const startTime = new Date().getTime(); const evaluationResults: EvaluationResult[] = []; - const predictionRequests = dataset.flatMap(({ input, reference }) => - agentExecutorEvaluators.map((agent) => ({ - input, - reference, - request: callAgentWithRetry({ agent, messages: [new HumanMessage(input)], logger }), - })) + const predictionRequests = dataset.flatMap(({ input, reference, id: exampleId }) => + agentExecutorEvaluators.map( + ({ agentEvaluator: agent, metadata: { connectorName, runName: agentRunName } }) => ({ + connectorName, + input, + reference, + exampleId, + request: () => + callAgentWithRetry({ agent, exampleId, messages: [new HumanMessage(input)], logger }), + runName: agentRunName, + }) + ) ); + const requestChunks = createChunks(maxConcurrency, predictionRequests); + const totalChunks = requestChunks.length; + logger.info(`Total prediction requests: ${predictionRequests.length}`); - logger.info(`Chunk size: ${maxConcurrency}`); + logger.info(`Chunk size (maxConcurrency): ${maxConcurrency}`); + logger.info(`Total chunks: ${totalChunks}`); logger.info('Fetching predictions...'); - const requestChunks = chunk(maxConcurrency, predictionRequests); - await asyncForEach(requestChunks, async (c, i) => { - logger.info(`Prediction request chunk: ${i + 1} of ${requestChunks.length}`); + + while (requestChunks.length) { + const chunk = requestChunks.shift() ?? []; + const chunkNumber = totalChunks - requestChunks.length; + logger.info(`Prediction request chunk: ${chunkNumber} of ${totalChunks}`); + logger.debug(chunk); // Note, order is kept between chunk and dataset, and is preserved w/ Promise.allSettled - const chunkResults = await Promise.allSettled(c.map((r) => r.request)); - logger.info(`Prediction request chunk ${i + 1} response:\n${JSON.stringify(chunkResults)}`); + const chunkResults = await Promise.allSettled(chunk.map((r) => r.request())); + logger.info( + `Prediction request chunk ${chunkNumber} response:\n${JSON.stringify(chunkResults)}` + ); chunkResults.forEach((response, chunkResultIndex) => evaluationResults.push({ '@timestamp': new Date().toISOString(), - input: c[chunkResultIndex].input, - reference: c[chunkResultIndex].reference, + connectorName: chunk[chunkResultIndex].connectorName, + input: chunk[chunkResultIndex].input, + inputExampleId: chunk[chunkResultIndex].exampleId, + reference: chunk[chunkResultIndex].reference, evaluationId, evaluation: {}, prediction: getMessageFromLangChainResponse(response), predictionResponse: response, + runName: chunk[chunkResultIndex].runName, }) ); - }); + } logger.info(`Prediction results:\n${JSON.stringify(evaluationResults)}`); + if (evaluatorModel == null) { + const endTime = new Date().getTime(); + + const evaluationSummary: EvaluationSummary = { + evaluationId, + '@timestamp': new Date().toISOString(), + evaluationStart: startTime, + evaluationEnd: endTime, + evaluationDuration: endTime - startTime, + runName, + totalAgents: agentExecutorEvaluators.length, + totalInput: dataset.length, + totalRequests: predictionRequests.length, + }; + + logger.info(`Final results:\n${JSON.stringify(evaluationResults)}`); + + return { evaluationResults, evaluationSummary }; + } + + // Continue with actual evaluation if expected logger.info('Performing evaluation....'); logger.info(`Evaluation model: ${evaluatorModel._llmType()}`); @@ -109,22 +157,53 @@ export const performEvaluation = async ({ criteria: 'correctness', llm: evaluatorModel, }); - await asyncForEach(evaluationResults, async ({ input, prediction, reference }, index) => { - // TODO: Rate limit evaluator calls, though haven't seen any `429`'s yet in testing datasets up to 10 w/ azure/bedrock - const evaluation = await evaluator.evaluateStrings({ - input, - prediction, - reference, + + for (const result of evaluationResults) { + const { input, inputExampleId: exampleId, prediction, reference } = result; + // Create an eval tracer so eval traces end up in the right project (runName in this instance as to correlate + // with the test run), don't supply `exampleID` as that results in a new Dataset `Test` run being created and + // polluting the `predictions` that ran above + const evalTracer = new LangChainTracer({ + projectName: runName, }); - evaluationResults[index].evaluation = evaluation; - await wait(1000); - }); + // Create RunCollector for uploading evals to LangSmith, no TS variant for `EvaluatorCallbackHandler` or + // `run_on_dataset` w/ eval config, so using `RunCollectorCallbackHandler` and then uploading manually via + // client.createFeedback() + // See: https://github.com/langchain-ai/langsmith-sdk/blob/18449e5848d85ac0a320f320c37f454f949de1e1/js/src/client.ts#L1249-L1256 + const runCollector = new RunCollectorCallbackHandler({ exampleId }); + const evaluation = await evaluator.evaluateStrings( + { + input, + prediction, + reference, + }, + { + callbacks: [...(isLangSmithEnabled() ? [evalTracer, runCollector] : [])], + tags: ['security-assistant-evaluation'], + } + ); + result.evaluation = evaluation; + + // Write to LangSmith + if (isLangSmithEnabled()) { + const langSmithLink = await writeLangSmithFeedback( + runCollector.tracedRuns[0], + evaluationId, + logger + ); + result.langSmithLink = langSmithLink; + } + } } else if (evaluationType === 'esql-validator') { logger.info('Evaluation type: esql-validator'); // TODO: Implement esql-validator here } else if (evaluationType === 'custom') { logger.info('Evaluation type: custom'); // TODO: Implement custom evaluation here + // const llm = new ChatOpenAI({ temperature: 0, tags: ["my-llm-tag"] }); + // const prompt = PromptTemplate.fromTemplate("Say {input}"); + // const chain = prompt.pipe(llm).withConfig( { tags: ["my-bash-tag", "another-tag"] }); + // await chain.invoke({ input: "Hello, World!"}, { tags: ["shared-tags"] }); } const endTime = new Date().getTime(); @@ -135,12 +214,13 @@ export const performEvaluation = async ({ evaluationStart: startTime, evaluationEnd: endTime, evaluationDuration: endTime - startTime, + runName, totalAgents: agentExecutorEvaluators.length, totalInput: dataset.length, totalRequests: predictionRequests.length, }; - logger.info(`Final results:\n${JSON.stringify(evaluationResults, null, 2)}`); + logger.info(`Final results:\n${JSON.stringify(evaluationResults)}`); return { evaluationResults, evaluationSummary }; }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/utils.ts b/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/utils.ts index af0f57de95a38..fd11928c90872 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/utils.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/model_evaluator/utils.ts @@ -15,19 +15,21 @@ export const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, export interface CallAgentWithRetryParams { agent: AgentExecutorEvaluator; + exampleId?: string; messages: BaseMessage[]; logger: Logger | ToolingLog; maxRetries?: number; } export const callAgentWithRetry = async ({ agent, + exampleId, messages, logger, maxRetries = 3, }: CallAgentWithRetryParams) => { for (let attempt = 0; attempt < maxRetries; attempt++) { try { - return await agent(messages); + return await agent(messages, exampleId); } catch (error) { // Check for 429, and then if there is a retry-after header const { isRateLimitError, retryAfter } = parseErrorMessage(error); diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts index 1b533e49c4cfe..52f820db17a01 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts @@ -18,13 +18,16 @@ import { PostEvaluateBody, PostEvaluatePathQuery } from '../../schemas/evaluate/ import { performEvaluation } from '../../lib/model_evaluator/evaluation'; import { callAgentExecutor } from '../../lib/langchain/execute_custom_llm_chain'; import { callOpenAIFunctionsExecutor } from '../../lib/langchain/executors/openai_functions_executor'; -import { AgentExecutor, AgentExecutorEvaluator } from '../../lib/langchain/executors/types'; +import { + AgentExecutor, + AgentExecutorEvaluatorWithMetadata, +} from '../../lib/langchain/executors/types'; import { ActionsClientLlm } from '../../lib/langchain/llm/actions_client_llm'; import { indexEvaluations, setupEvaluationIndex, } from '../../lib/model_evaluator/output_index/utils'; -import { getLlmType } from './utils'; +import { fetchLangSmithDataset, getConnectorName, getLangSmithTracer, getLlmType } from './utils'; import { RequestBody } from '../../lib/langchain/types'; /** @@ -50,34 +53,41 @@ export const postEvaluateRoute = ( }, async (context, request, response) => { // TODO: Limit route based on experimental feature - const resp = buildResponse(response); const logger: Logger = (await context.elasticAssistant).logger; - - const { evalModel, evaluationType, outputIndex } = request.query; - const { dataset, evalPrompt } = request.body; - const connectorIds = request.query.models?.split(',') || []; - const agentNames = request.query.agents?.split(',') || []; - - const evaluationId = uuidv4(); - - logger.info('postEvaluateRoute:'); - logger.info(`request.query:\n${JSON.stringify(request.query, null, 2)}`); - logger.info(`request.body:\n${JSON.stringify(request.body, null, 2)}`); - logger.info(`Evaluation ID: ${evaluationId}`); - - const totalExecutions = connectorIds.length * agentNames.length * dataset.length; - logger.info('Creating agents:'); - logger.info(`\tconnectors/models: ${connectorIds.length}`); - logger.info(`\tagents: ${agentNames.length}`); - logger.info(`\tdataset: ${dataset.length}`); - logger.warn(`\ttotal baseline agent executions: ${totalExecutions} `); - if (totalExecutions > 50) { - logger.warn( - `Total baseline agent executions >= 50! This may take a while, and cost some money...` - ); - } - try { + const evaluationId = uuidv4(); + const { + evalModel, + evaluationType, + outputIndex, + datasetName, + projectName = 'default', + runName = evaluationId, + } = request.query; + const { dataset: customDataset = [], evalPrompt } = request.body; + const connectorIds = request.query.models?.split(',') || []; + const agentNames = request.query.agents?.split(',') || []; + + const dataset = + datasetName != null ? await fetchLangSmithDataset(datasetName, logger) : customDataset; + + logger.info('postEvaluateRoute:'); + logger.info(`request.query:\n${JSON.stringify(request.query, null, 2)}`); + logger.info(`request.body:\n${JSON.stringify(request.body, null, 2)}`); + logger.info(`Evaluation ID: ${evaluationId}`); + + const totalExecutions = connectorIds.length * agentNames.length * dataset.length; + logger.info('Creating agents:'); + logger.info(`\tconnectors/models: ${connectorIds.length}`); + logger.info(`\tagents: ${agentNames.length}`); + logger.info(`\tdataset: ${dataset.length}`); + logger.warn(`\ttotal baseline agent executions: ${totalExecutions} `); + if (totalExecutions > 50) { + logger.warn( + `Total baseline agent executions >= 50! This may take a while, and cost some money...` + ); + } + // Get the actions plugin start contract from the request context for the agents const actions = (await context.elasticAssistant).actions; @@ -112,35 +122,58 @@ export const postEvaluateRoute = ( // Create an array of executor functions to call in batches // One for each connector/model + agent combination // Hoist `langChainMessages` so they can be batched by dataset.input in the evaluator - const agents: AgentExecutorEvaluator[] = []; + const agents: AgentExecutorEvaluatorWithMetadata[] = []; connectorIds.forEach((connectorId) => { agentNames.forEach((agentName) => { logger.info(`Creating agent: ${connectorId} + ${agentName}`); const llmType = getLlmType(connectorId, connectors); - agents.push((langChainMessages) => - AGENT_EXECUTOR_MAP[agentName]({ - actions, - connectorId, - esClient, - elserId, - langChainMessages, - llmType, - logger, - request: skeletonRequest, - kbResource: ESQL_RESOURCE, - }) - ); + const connectorName = + getConnectorName(connectorId, connectors) ?? '[unknown connector]'; + const detailedRunName = `${runName} - ${connectorName} + ${agentName}`; + agents.push({ + agentEvaluator: (langChainMessages, exampleId) => + AGENT_EXECUTOR_MAP[agentName]({ + actions, + connectorId, + esClient, + elserId, + langChainMessages, + llmType, + logger, + request: skeletonRequest, + kbResource: ESQL_RESOURCE, + traceOptions: { + exampleId, + projectName, + runName: detailedRunName, + evaluationId, + tags: [ + 'security-assistant-prediction', + ...(connectorName != null ? [connectorName] : []), + runName, + ], + tracers: getLangSmithTracer(detailedRunName, exampleId, logger), + }, + }), + metadata: { + connectorName, + runName: detailedRunName, + }, + }); }); }); - logger.info(`Agents created: ${agents.length}`); - const evaluatorModel = new ActionsClientLlm({ - actions, - connectorId: evalModel, - request: skeletonRequest, - logger, - }); + // Evaluator Model is optional to support just running predictions + const evaluatorModel = + evalModel == null || evalModel === '' + ? undefined + : new ActionsClientLlm({ + actions, + connectorId: evalModel, + request: skeletonRequest, + logger, + }); const { evaluationResults, evaluationSummary } = await performEvaluation({ agentExecutorEvaluators: agents, @@ -150,6 +183,7 @@ export const postEvaluateRoute = ( evaluationPrompt: evalPrompt, evaluationType, logger, + runName, }); logger.info(`Writing evaluation results to index: ${outputIndex}`); @@ -163,14 +197,15 @@ export const postEvaluateRoute = ( }); return response.ok({ - body: { success: true }, + body: { evaluationId, success: true }, }); } catch (err) { logger.error(err); const error = transformError(err); + const resp = buildResponse(response); return resp.error({ - body: error.message, + body: { success: false, error: error.message }, statusCode: error.statusCode, }); } diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts index f208fe1d5739f..550e89667256e 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/utils.ts @@ -5,8 +5,14 @@ * 2.0. */ +import { Client } from 'langsmith'; import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; import type { ActionResult } from '@kbn/actions-plugin/server'; +import type { Logger } from '@kbn/core/server'; +import type { Run } from 'langsmith/schemas'; +import { ToolingLog } from '@kbn/tooling-log'; +import { LangChainTracer } from 'langchain/callbacks'; +import { Dataset } from '../../schemas/evaluate/post_evaluate'; /** * Returns the LangChain `llmType` for the given connectorId/connectors @@ -29,3 +35,133 @@ export const getLlmType = (connectorId: string, connectors: ActionResult[]): str return undefined; }; + +/** + * Return connector name for the given connectorId/connectors + * + * @param connectorId + * @param connectors + */ +export const getConnectorName = ( + connectorId: string, + connectors: ActionResult[] +): string | undefined => { + return connectors.find((c) => c.id === connectorId)?.name; +}; + +/** + * Fetches a dataset from LangSmith. Note that `client` will use env vars + * + * @param datasetName + * @param logger + */ +export const fetchLangSmithDataset = async ( + datasetName: string | undefined, + logger: Logger +): Promise => { + if (datasetName === undefined || !isLangSmithEnabled()) { + throw new Error('LangSmith dataset name not provided or LangSmith not enabled'); + } + + try { + const client = new Client(); + + const examples = []; + for await (const example of client.listExamples({ datasetName })) { + examples.push(example); + } + + // Convert to internal Dataset type -- TODO: add generic support for the different LangSmith test dataset formats + const dataset: Dataset = examples.map((example) => ({ + id: example.id, + input: example.inputs.input as string, + reference: (example.outputs?.output as string) ?? '', + tags: [], // TODO: Consider adding tags from example data, e.g.: `datasetId:${example.dataset_id}`, `exampleName:${example.name}` + prediction: undefined, + })); + + return dataset; + } catch (e) { + logger.error(`Error fetching dataset from LangSmith: ${e.message}`); + return []; + } +}; + +/** + * Write Feedback to LangSmith for a given Run + * + * @param run + * @param evaluationId + * @param logger + */ +export const writeLangSmithFeedback = async ( + run: Run, + evaluationId: string, + logger: Logger | ToolingLog +): Promise => { + try { + const client = new Client(); + const feedback = { + score: run.feedback_stats?.score, + value: run.feedback_stats?.value, + correction: run.feedback_stats?.correction, + comment: run.feedback_stats?.comment, + sourceInfo: run.feedback_stats?.sourceInfo, + feedbackSourceType: run.feedback_stats?.feedbackSourceType, + sourceRunId: run.feedback_stats?.sourceRunId, + feedbackId: run.feedback_stats?.feedbackId, + eager: run.feedback_stats?.eager, + }; + await client.createFeedback(run.id, evaluationId, feedback); + const runUrl = await client.getRunUrl({ run }); + return runUrl; + } catch (e) { + logger.error(`Error writing feedback to LangSmith: ${e.message}`); + return ''; + } +}; + +/** + * Returns a custom LangChainTracer which adds the `exampleId` so Dataset 'Test' runs are written to LangSmith + * If `exampleId` is present (and a corresponding example exists in LangSmith) trace is written to the Dataset's `Tests` + * section, otherwise it is written to the `Project` provided + * + * @param projectName Name of project to trace results to + * @param exampleId Dataset exampleId to associate trace with + * @param logger + */ +export const getLangSmithTracer = ( + projectName: string | undefined, + exampleId: string | undefined, + logger: Logger | ToolingLog +): LangChainTracer[] => { + try { + if (!isLangSmithEnabled()) { + return []; + } + const lcTracer = new LangChainTracer({ + projectName: projectName ?? 'default', // Shows as the 'test' run's 'name' in langsmith ui + exampleId, + }); + + return [lcTracer]; + } catch (e) { + // Note: creating a tracer can fail if the LangSmith env vars are not set correctly + logger.error(`Error creating LangSmith tracer: ${e.message}`); + } + + return []; +}; + +/** + * Returns true if LangSmith/tracing is enabled + */ +export const isLangSmithEnabled = (): boolean => { + try { + // Just checking if apiKey is available, if better way to check for enabled that is not env var please update + const config = Client.getDefaultClientConfig(); + return config.apiKey != null; + } catch (e) { + return false; + } +}; diff --git a/x-pack/plugins/elastic_assistant/server/schemas/evaluate/post_evaluate.ts b/x-pack/plugins/elastic_assistant/server/schemas/evaluate/post_evaluate.ts index f006fe091bf14..c9c0ee1f00e51 100644 --- a/x-pack/plugins/elastic_assistant/server/schemas/evaluate/post_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/schemas/evaluate/post_evaluate.ts @@ -26,16 +26,21 @@ const outputIndex = new t.Type( /** Validates the URL path of a POST request to the `/evaluate` endpoint */ export const PostEvaluatePathQuery = t.type({ agents: t.string, - evaluationType: t.string, - evalModel: t.string, - outputIndex, + datasetName: t.union([t.string, t.undefined]), + evaluationType: t.union([t.string, t.undefined]), + evalModel: t.union([t.string, t.undefined]), models: t.string, + outputIndex, + projectName: t.union([t.string, t.undefined]), + runName: t.union([t.string, t.undefined]), }); export type DatasetItem = t.TypeOf; export const DatasetItem = t.type({ + id: t.union([t.string, t.undefined]), input: t.string, reference: t.string, + tags: t.union([t.array(t.string), t.undefined]), prediction: t.union([t.string, t.undefined]), }); @@ -44,7 +49,7 @@ export const Dataset = t.array(DatasetItem); /** Validates the body of a POST request to the `/evaluate` endpoint */ export const PostEvaluateBody = t.type({ - dataset: Dataset, + dataset: t.union([Dataset, t.undefined]), evalPrompt: t.union([t.string, t.undefined]), }); diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json index 45dec8428e8cc..53616fc2dc2b0 100644 --- a/x-pack/plugins/elastic_assistant/tsconfig.json +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -29,9 +29,9 @@ "@kbn/tooling-log", "@kbn/core-elasticsearch-server", "@kbn/logging", - "@kbn/std", "@kbn/stack-connectors-plugin", "@kbn/ml-plugin", + "@kbn/apm-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts index 321b93c8452ee..7bde0d6190547 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts @@ -10,7 +10,7 @@ import stringify from 'json-stable-stringify'; import typeDetect from 'type-detect'; import type { Logger } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { EncryptedSavedObjectAttributesDefinition } from './encrypted_saved_object_type_definition'; import { EncryptionError, EncryptionErrorOperation } from './encryption_error'; diff --git a/x-pack/plugins/enterprise_search/README.md b/x-pack/plugins/enterprise_search/README.md index af0cdd43d97b8..bc49c47fe6880 100644 --- a/x-pack/plugins/enterprise_search/README.md +++ b/x-pack/plugins/enterprise_search/README.md @@ -137,4 +137,4 @@ To track what Cypress is doing while running tests, you can pass in `--config vi See [our functional test runner README](../../test/functional_enterprise_search). -Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/enterprise_search.ts). \ No newline at end of file +Our automated accessibility tests can be found in [x-pack/test/accessibility/apps](../../test/accessibility/apps/group3/enterprise_search.ts). \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/common/types/ml.ts b/x-pack/plugins/enterprise_search/common/types/ml.ts index a09ad4129c8c0..5723d4383d1ea 100644 --- a/x-pack/plugins/enterprise_search/common/types/ml.ts +++ b/x-pack/plugins/enterprise_search/common/types/ml.ts @@ -22,3 +22,25 @@ export interface MlModelDeploymentStatus { targetAllocationCount: number; threadsPerAllocation: number; } + +export interface MlModel { + modelId: string; + /** Model inference type, e.g. ner, text_classification */ + type: string; + title: string; + description?: string; + license?: string; + modelDetailsPageUrl?: string; + deploymentState: MlModelDeploymentState; + deploymentStateReason?: string; + startTime: number; + targetAllocationCount: number; + nodeAllocationCount: number; + threadsPerAllocation: number; + /** Is this model one of the promoted ones (e.g. ELSER, E5)? */ + isPromoted?: boolean; + /** Does this model object act as a placeholder before installing the model? */ + isPlaceholder: boolean; + /** Does this model have deployment stats? */ + hasStats: boolean; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.test.ts new file mode 100644 index 0000000000000..6d66ed5704721 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright 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 { LogicMounter } from '../../../__mocks__/kea_logic'; + +import { HttpError, Status } from '../../../../../common/types/api'; +import { MlModelDeploymentState } from '../../../../../common/types/ml'; + +import { MlModel } from '../../../../../common/types/ml'; + +import { + CachedFetchModelsApiLogic, + CachedFetchModelsApiLogicValues, +} from './cached_fetch_models_api_logic'; +import { FetchModelsApiLogic } from './fetch_models_api_logic'; + +const DEFAULT_VALUES: CachedFetchModelsApiLogicValues = { + data: [], + isInitialLoading: false, + isLoading: false, + modelsData: null, + pollTimeoutId: null, + status: Status.IDLE, +}; + +const FETCH_MODELS_API_DATA_RESPONSE: MlModel[] = [ + { + modelId: 'model_1', + title: 'Model 1', + type: 'ner', + deploymentState: MlModelDeploymentState.NotDeployed, + startTime: 0, + targetAllocationCount: 0, + nodeAllocationCount: 0, + threadsPerAllocation: 0, + isPlaceholder: false, + hasStats: false, + }, +]; +const FETCH_MODELS_API_ERROR_RESPONSE = { + body: { + error: 'Error while fetching models', + message: 'Error while fetching models', + statusCode: 500, + }, +} as HttpError; + +jest.useFakeTimers(); + +describe('TextExpansionCalloutLogic', () => { + const { mount } = new LogicMounter(CachedFetchModelsApiLogic); + const { mount: mountFetchModelsApiLogic } = new LogicMounter(FetchModelsApiLogic); + + beforeEach(() => { + jest.clearAllMocks(); + mountFetchModelsApiLogic(); + mount(); + }); + + describe('listeners', () => { + describe('apiError', () => { + it('sets new polling timeout if a timeout ID is already set', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + jest.spyOn(CachedFetchModelsApiLogic.actions, 'createPollTimeout'); + + CachedFetchModelsApiLogic.actions.apiError(FETCH_MODELS_API_ERROR_RESPONSE); + + expect(CachedFetchModelsApiLogic.actions.createPollTimeout).toHaveBeenCalled(); + }); + }); + + describe('apiSuccess', () => { + it('sets new polling timeout if a timeout ID is already set', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + jest.spyOn(CachedFetchModelsApiLogic.actions, 'createPollTimeout'); + + CachedFetchModelsApiLogic.actions.apiSuccess(FETCH_MODELS_API_DATA_RESPONSE); + + expect(CachedFetchModelsApiLogic.actions.createPollTimeout).toHaveBeenCalled(); + }); + }); + + describe('createPollTimeout', () => { + const duration = 5000; + it('clears polling timeout if it is set', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + jest.spyOn(global, 'clearTimeout'); + + CachedFetchModelsApiLogic.actions.createPollTimeout(duration); + + expect(clearTimeout).toHaveBeenCalledWith('timeout-id'); + }); + it('sets polling timeout', () => { + jest.spyOn(global, 'setTimeout'); + jest.spyOn(CachedFetchModelsApiLogic.actions, 'setTimeoutId'); + + CachedFetchModelsApiLogic.actions.createPollTimeout(duration); + + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), duration); + expect(CachedFetchModelsApiLogic.actions.setTimeoutId).toHaveBeenCalled(); + }); + }); + + describe('startPolling', () => { + it('clears polling timeout if it is set', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + jest.spyOn(global, 'clearTimeout'); + + CachedFetchModelsApiLogic.actions.startPolling(); + + expect(clearTimeout).toHaveBeenCalledWith('timeout-id'); + }); + it('makes API request and sets polling timeout', () => { + jest.spyOn(CachedFetchModelsApiLogic.actions, 'makeRequest'); + jest.spyOn(CachedFetchModelsApiLogic.actions, 'createPollTimeout'); + + CachedFetchModelsApiLogic.actions.startPolling(); + + expect(CachedFetchModelsApiLogic.actions.makeRequest).toHaveBeenCalled(); + expect(CachedFetchModelsApiLogic.actions.createPollTimeout).toHaveBeenCalled(); + }); + }); + + describe('stopPolling', () => { + it('clears polling timeout if it is set', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + jest.spyOn(global, 'clearTimeout'); + + CachedFetchModelsApiLogic.actions.stopPolling(); + + expect(clearTimeout).toHaveBeenCalledWith('timeout-id'); + }); + it('clears polling timeout value', () => { + jest.spyOn(CachedFetchModelsApiLogic.actions, 'clearPollTimeout'); + + CachedFetchModelsApiLogic.actions.stopPolling(); + + expect(CachedFetchModelsApiLogic.actions.clearPollTimeout).toHaveBeenCalled(); + }); + }); + }); + + describe('reducers', () => { + describe('modelsData', () => { + it('gets cleared on API reset', () => { + mount({ + ...DEFAULT_VALUES, + modelsData: [], + }); + + CachedFetchModelsApiLogic.actions.apiReset(); + + expect(CachedFetchModelsApiLogic.values.modelsData).toBe(null); + }); + it('gets set on API success', () => { + CachedFetchModelsApiLogic.actions.apiSuccess(FETCH_MODELS_API_DATA_RESPONSE); + + expect(CachedFetchModelsApiLogic.values.modelsData).toEqual(FETCH_MODELS_API_DATA_RESPONSE); + }); + }); + + describe('pollTimeoutId', () => { + it('gets cleared on clear timeout action', () => { + mount({ + ...DEFAULT_VALUES, + pollTimeoutId: 'timeout-id', + }); + + CachedFetchModelsApiLogic.actions.clearPollTimeout(); + + expect(CachedFetchModelsApiLogic.values.pollTimeoutId).toBe(null); + }); + it('gets set on set timeout action', () => { + const timeout = setTimeout(() => {}, 500); + + CachedFetchModelsApiLogic.actions.setTimeoutId(timeout); + + expect(CachedFetchModelsApiLogic.values.pollTimeoutId).toEqual(timeout); + }); + }); + }); + + describe('selectors', () => { + describe('isInitialLoading', () => { + it('true if API is idle', () => { + mount(DEFAULT_VALUES); + + expect(CachedFetchModelsApiLogic.values.isInitialLoading).toBe(true); + }); + it('true if API is loading for the first time', () => { + mount({ + ...DEFAULT_VALUES, + status: Status.LOADING, + }); + + expect(CachedFetchModelsApiLogic.values.isInitialLoading).toBe(true); + }); + it('false if the API is neither idle nor loading', () => { + CachedFetchModelsApiLogic.actions.apiSuccess(FETCH_MODELS_API_DATA_RESPONSE); + + expect(CachedFetchModelsApiLogic.values.isInitialLoading).toBe(false); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.ts new file mode 100644 index 0000000000000..d65af6ec2fcf4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/cached_fetch_models_api_logic.ts @@ -0,0 +1,125 @@ +/* + * Copyright 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 { kea, MakeLogicType } from 'kea'; + +import { isEqual } from 'lodash'; + +import { Status } from '../../../../../common/types/api'; +import { MlModel } from '../../../../../common/types/ml'; +import { Actions } from '../../../shared/api_logic/create_api_logic'; + +import { FetchModelsApiLogic, FetchModelsApiResponse } from './fetch_models_api_logic'; + +const FETCH_MODELS_POLLING_DURATION = 5000; // 5 seconds +const FETCH_MODELS_POLLING_DURATION_ON_FAILURE = 30000; // 30 seconds + +export interface CachedFetchModlesApiLogicActions { + apiError: Actions<{}, FetchModelsApiResponse>['apiError']; + apiReset: Actions<{}, FetchModelsApiResponse>['apiReset']; + apiSuccess: Actions<{}, FetchModelsApiResponse>['apiSuccess']; + clearPollTimeout(): void; + createPollTimeout(duration: number): { duration: number }; + makeRequest: Actions<{}, FetchModelsApiResponse>['makeRequest']; + setTimeoutId(id: NodeJS.Timeout): { id: NodeJS.Timeout }; + startPolling(): void; + stopPolling(): void; +} + +export interface CachedFetchModelsApiLogicValues { + data: FetchModelsApiResponse; + isInitialLoading: boolean; + isLoading: boolean; + modelsData: MlModel[] | null; + pollTimeoutId: NodeJS.Timeout | null; + status: Status; +} + +export const CachedFetchModelsApiLogic = kea< + MakeLogicType +>({ + actions: { + clearPollTimeout: true, + createPollTimeout: (duration) => ({ duration }), + setTimeoutId: (id) => ({ id }), + startPolling: true, + stopPolling: true, + }, + connect: { + actions: [FetchModelsApiLogic, ['apiSuccess', 'apiError', 'apiReset', 'makeRequest']], + values: [FetchModelsApiLogic, ['data', 'status']], + }, + events: ({ values }) => ({ + beforeUnmount: () => { + if (values.pollTimeoutId) { + clearTimeout(values.pollTimeoutId); + } + }, + }), + listeners: ({ actions, values }) => ({ + apiError: () => { + if (values.pollTimeoutId) { + actions.createPollTimeout(FETCH_MODELS_POLLING_DURATION_ON_FAILURE); + } + }, + apiSuccess: () => { + if (values.pollTimeoutId) { + actions.createPollTimeout(FETCH_MODELS_POLLING_DURATION); + } + }, + createPollTimeout: ({ duration }) => { + if (values.pollTimeoutId) { + clearTimeout(values.pollTimeoutId); + } + + const timeoutId = setTimeout(() => { + actions.makeRequest({}); + }, duration); + actions.setTimeoutId(timeoutId); + }, + startPolling: () => { + if (values.pollTimeoutId) { + clearTimeout(values.pollTimeoutId); + } + actions.makeRequest({}); + actions.createPollTimeout(FETCH_MODELS_POLLING_DURATION); + }, + stopPolling: () => { + if (values.pollTimeoutId) { + clearTimeout(values.pollTimeoutId); + } + actions.clearPollTimeout(); + }, + }), + path: ['enterprise_search', 'content', 'api', 'fetch_models_api_wrapper'], + reducers: { + modelsData: [ + null, + { + apiReset: () => null, + apiSuccess: (currentState, newState) => + isEqual(currentState, newState) ? currentState : newState, + }, + ], + pollTimeoutId: [ + null, + { + clearPollTimeout: () => null, + setTimeoutId: (_, { id }) => id, + }, + ], + }, + selectors: ({ selectors }) => ({ + isInitialLoading: [ + () => [selectors.status, selectors.modelsData], + ( + status: CachedFetchModelsApiLogicValues['status'], + modelsData: CachedFetchModelsApiLogicValues['modelsData'] + ) => status === Status.IDLE || (modelsData === null && status === Status.LOADING), + ], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.test.ts new file mode 100644 index 0000000000000..78ef03010ac2c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../__mocks__/kea_logic'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { createModel } from './create_model_api_logic'; + +describe('CreateModelApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('createModel', () => { + it('calls correct api', async () => { + const mockResponseBody = { modelId: 'model_1', deploymentState: '' }; + http.post.mockReturnValue(Promise.resolve(mockResponseBody)); + + const result = createModel({ modelId: 'model_1' }); + await nextTick(); + expect(http.post).toHaveBeenCalledWith('/internal/enterprise_search/ml/models/model_1'); + await expect(result).resolves.toEqual(mockResponseBody); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.ts new file mode 100644 index 0000000000000..6852e56cea674 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/create_model_api_logic.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface CreateModelArgs { + modelId: string; +} + +export interface CreateModelResponse { + deploymentState: string; + modelId: string; +} + +export const createModel = async ({ modelId }: CreateModelArgs): Promise => { + const route = `/internal/enterprise_search/ml/models/${modelId}`; + return await HttpLogic.values.http.post(route); +}; + +export const CreateModelApiLogic = createApiLogic(['create_model_api_logic'], createModel, { + showErrorFlash: false, +}); + +export type CreateModelApiLogicActions = Actions; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.test.ts new file mode 100644 index 0000000000000..77f2a0548fa6c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../__mocks__/kea_logic'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { fetchModels } from './fetch_models_api_logic'; + +describe('FetchModelsApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetchModels', () => { + it('calls correct api', async () => { + const mockResponseBody = [{ modelId: 'model_1' }, { modelId: 'model_2' }]; + http.get.mockReturnValue(Promise.resolve(mockResponseBody)); + + const result = fetchModels(); + await nextTick(); + expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/ml/models'); + await expect(result).resolves.toEqual(mockResponseBody); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.ts new file mode 100644 index 0000000000000..751a03546b059 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/fetch_models_api_logic.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 { MlModel } from '../../../../../common/types/ml'; +import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export type FetchModelsApiResponse = MlModel[]; + +export const fetchModels = async () => { + const route = '/internal/enterprise_search/ml/models'; + return await HttpLogic.values.http.get(route); +}; + +export const FetchModelsApiLogic = createApiLogic(['fetch_models_api_logic'], fetchModels, { + showErrorFlash: false, +}); + +export type FetchModelsApiLogicActions = Actions<{}, FetchModelsApiResponse>; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.test.ts new file mode 100644 index 0000000000000..0c9d9da875e52 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../__mocks__/kea_logic'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { startModel } from './start_model_api_logic'; + +describe('StartModelApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('startModel', () => { + it('calls correct api', async () => { + const mockResponseBody = { modelId: 'model_1', deploymentState: 'started' }; + http.post.mockReturnValue(Promise.resolve(mockResponseBody)); + + const result = startModel({ modelId: 'model_1' }); + await nextTick(); + expect(http.post).toHaveBeenCalledWith( + '/internal/enterprise_search/ml/models/model_1/deploy' + ); + await expect(result).resolves.toEqual(mockResponseBody); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.ts new file mode 100644 index 0000000000000..333b23cd65242 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/start_model_api_logic.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface StartModelArgs { + modelId: string; +} + +export interface StartModelResponse { + deploymentState: string; + modelId: string; +} + +export const startModel = async ({ modelId }: StartModelArgs): Promise => { + const route = `/internal/enterprise_search/ml/models/${modelId}/deploy`; + return await HttpLogic.values.http.post(route); +}; + +export const StartModelApiLogic = createApiLogic(['start_model_api_logic'], startModel, { + showErrorFlash: false, +}); + +export type StartModelApiLogicActions = Actions; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx index b2932c9547e27..999bd333a0ffb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/documents.tsx @@ -26,7 +26,6 @@ import { ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT, } from '../../../../../common/constants'; import { Status } from '../../../../../common/types/api'; -import { stripSearchPrefix } from '../../../../../common/utils/strip_search_prefix'; import { DEFAULT_META } from '../../../shared/constants'; import { KibanaLogic } from '../../../shared/kibana'; @@ -69,7 +68,7 @@ export const SearchIndexDocuments: React.FC = () => { const indexToShow = selectedIndexType === 'content-index' ? indexName - : stripSearchPrefix(indexName, CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX); + : `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexName}`; const mappingLogic = mappingsWithPropsApiLogic(indexToShow); const documentLogic = searchDocumentsApiLogic(indexToShow); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.test.tsx index 2a3432d4d523b..68bf7fc48e7dd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.test.tsx @@ -33,7 +33,6 @@ import { NoModelsPanel } from './no_models'; import { ReviewPipeline } from './review_pipeline'; import { TestPipeline } from './test_pipeline'; import { AddInferencePipelineSteps } from './types'; -import { UpdateMappings } from './update_mappings'; const supportedMLModels: TrainedModelConfigResponse[] = [ { @@ -122,16 +121,6 @@ describe('AddInferencePipelineFlyout', () => { const wrapper = shallow(); expect(wrapper.find(ConfigureFields)).toHaveLength(1); }); - it('renders mappings step', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { - step: AddInferencePipelineSteps.Mappings, - }, - }); - const wrapper = shallow(); - expect(wrapper.find(UpdateMappings)).toHaveLength(1); - }); it('renders test step', () => { setMockValues({ ...DEFAULT_VALUES, @@ -156,9 +145,8 @@ describe('AddInferencePipelineFlyout', () => { describe('AddInferencePipelineHorizontalSteps', () => { const CONFIGURE_STEP_INDEX = 0; const FIELDS_STEP_INDEX = 1; - const MAPPINGS_STEP_INDEX = 2; - const TEST_STEP_INDEX = 3; - const REVIEW_STEP_INDEX = 4; + const TEST_STEP_INDEX = 2; + const REVIEW_STEP_INDEX = 3; const onAddInferencePipelineStepChange = jest.fn(); beforeEach(() => { setMockActions({ @@ -225,24 +213,6 @@ describe('AddInferencePipelineFlyout', () => { }); testStepStatus(FIELDS_STEP_INDEX, 'Fields', 'complete'); }); - it('mappings step is current when on step', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { - step: AddInferencePipelineSteps.Mappings, - }, - }); - testStepStatus(MAPPINGS_STEP_INDEX, 'Mappings', 'current'); - }); - it('mappings step is complete when on later step', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { - step: AddInferencePipelineSteps.Review, - }, - }); - testStepStatus(MAPPINGS_STEP_INDEX, 'Mappings', 'complete'); - }); it('test step is current when on step', () => { setMockValues({ ...DEFAULT_VALUES, @@ -294,9 +264,6 @@ describe('AddInferencePipelineFlyout', () => { it('clicking fields step updates step', () => { testClickStep(FIELDS_STEP_INDEX, AddInferencePipelineSteps.Fields); }); - it('clicking mappings step updates step', () => { - testClickStep(MAPPINGS_STEP_INDEX, AddInferencePipelineSteps.Mappings); - }); it('clicking test step updates step', () => { testClickStep(TEST_STEP_INDEX, AddInferencePipelineSteps.Test); }); @@ -319,13 +286,6 @@ describe('AddInferencePipelineFlyout', () => { }); testCannotClickInvalidStep(FIELDS_STEP_INDEX); }); - it('cannot click mappings step when data is invalid', () => { - setMockValues({ - ...DEFAULT_VALUES, - isPipelineDataValid: false, - }); - testCannotClickInvalidStep(MAPPINGS_STEP_INDEX); - }); it('cannot click test step when data is invalid', () => { setMockValues({ ...DEFAULT_VALUES, @@ -378,23 +338,6 @@ describe('AddInferencePipelineFlyout', () => { cancelBtn.prop('onClick')!({} as any); expect(onClose).toHaveBeenCalledTimes(1); }); - it('renders cancel button on mappings step', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { - ...DEFAULT_VALUES.addInferencePipelineModal, - step: AddInferencePipelineSteps.Mappings, - }, - }); - const wrapper = shallow( - - ); - expect(wrapper.find(EuiButtonEmpty)).toHaveLength(2); - const cancelBtn = wrapper.find(EuiButtonEmpty).at(0); - expect(cancelBtn.prop('children')).toBe('Cancel'); - cancelBtn.prop('onClick')!({} as any); - expect(onClose).toHaveBeenCalledTimes(1); - }); it('renders cancel button on test step', () => { setMockValues({ ...DEFAULT_VALUES, @@ -456,11 +399,8 @@ describe('AddInferencePipelineFlyout', () => { it('renders back button on fields step', () => { testBackButton(AddInferencePipelineSteps.Fields, AddInferencePipelineSteps.Configuration); }); - it('renders back button on mappings step', () => { - testBackButton(AddInferencePipelineSteps.Mappings, AddInferencePipelineSteps.Fields); - }); it('renders back button on test step', () => { - testBackButton(AddInferencePipelineSteps.Test, AddInferencePipelineSteps.Mappings); + testBackButton(AddInferencePipelineSteps.Test, AddInferencePipelineSteps.Fields); }); it('renders back button on review step', () => { testBackButton(AddInferencePipelineSteps.Review, AddInferencePipelineSteps.Test); @@ -504,26 +444,6 @@ describe('AddInferencePipelineFlyout', () => { expect(contBtn.prop('children')).toBe('Continue'); expect(contBtn.prop('disabled')).toBe(false); contBtn.prop('onClick')!({} as any); - expect(actions.onAddInferencePipelineStepChange).toHaveBeenCalledWith( - AddInferencePipelineSteps.Mappings - ); - }); - it('renders continue button on mappings step', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { - ...DEFAULT_VALUES.addInferencePipelineModal, - step: AddInferencePipelineSteps.Mappings, - }, - }); - const wrapper = shallow( - - ); - const contBtn = wrapper.find(EuiButton); - expect(contBtn).toHaveLength(1); - expect(contBtn.prop('children')).toBe('Continue'); - expect(contBtn.prop('disabled')).toBe(false); - contBtn.prop('onClick')!({} as any); expect(actions.onAddInferencePipelineStepChange).toHaveBeenCalledWith( AddInferencePipelineSteps.Test ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.tsx index 85ef342dcaa52..654cdafad35eb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/add_inference_pipeline_flyout.tsx @@ -47,7 +47,6 @@ import { TestPipeline } from './test_pipeline'; import { AddInferencePipelineSteps } from './types'; import './add_inference_pipeline_flyout.scss'; -import { UpdateMappings } from './update_mappings'; export interface AddInferencePipelineFlyoutProps { onClose: () => void; @@ -132,7 +131,6 @@ export const AddInferencePipelineContent = ({ onClose }: AddInferencePipelineFly {step === AddInferencePipelineSteps.Configuration && } {step === AddInferencePipelineSteps.Fields && } - {step === AddInferencePipelineSteps.Mappings && } {step === AddInferencePipelineSteps.Test && } {step === AddInferencePipelineSteps.Review && } @@ -191,22 +189,6 @@ export const AddInferencePipelineHorizontalSteps: React.FC = () => { } ), }, - { - // Mappings - onClick: () => { - if (!isPipelineDataValid) return; - onAddInferencePipelineStepChange(AddInferencePipelineSteps.Mappings); - }, - status: isPipelineDataValid - ? getStepStatus(step, AddInferencePipelineSteps.Mappings) - : 'disabled', - title: i18n.translate( - 'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.updateMappings.title', - { - defaultMessage: 'Mappings', - } - ), - }, { // Test onClick: () => { @@ -265,18 +247,13 @@ export const AddInferencePipelineFooter: React.FC< isContinueButtonEnabled = isConfigureStepValid; break; case AddInferencePipelineSteps.Fields: - nextStep = AddInferencePipelineSteps.Mappings; + nextStep = AddInferencePipelineSteps.Test; previousStep = AddInferencePipelineSteps.Configuration; isContinueButtonEnabled = isPipelineDataValid; break; - case AddInferencePipelineSteps.Mappings: - nextStep = AddInferencePipelineSteps.Test; - previousStep = AddInferencePipelineSteps.Fields; - isContinueButtonEnabled = true; - break; case AddInferencePipelineSteps.Test: nextStep = AddInferencePipelineSteps.Review; - previousStep = AddInferencePipelineSteps.Mappings; + previousStep = AddInferencePipelineSteps.Fields; isContinueButtonEnabled = true; break; case AddInferencePipelineSteps.Review: diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx index 37a7c71535869..444fd87ef4160 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx @@ -5,57 +5,51 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { useValues, useActions } from 'kea'; import { EuiFieldText, - EuiFlexGroup, - EuiFlexItem, EuiForm, EuiFormRow, - EuiLink, - EuiSelect, EuiSuperSelect, EuiSuperSelectOption, EuiSpacer, + EuiTabbedContent, + EuiTabbedContentTab, EuiTitle, EuiText, - EuiPanel, - EuiHorizontalRule, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { docLinks } from '../../../../../shared/doc_links'; - import { IndexNameLogic } from '../../index_name_logic'; import { IndexViewLogic } from '../../index_view_logic'; -import { InferenceConfiguration } from './inference_config'; import { EMPTY_PIPELINE_CONFIGURATION, MLInferenceLogic } from './ml_inference_logic'; import { MlModelSelectOption } from './model_select_option'; import { PipelineSelectOption } from './pipeline_select_option'; -import { TextExpansionCallOut } from './text_expansion_callout'; -import { MODEL_REDACTED_VALUE, MODEL_SELECT_PLACEHOLDER } from './utils'; +import { MODEL_REDACTED_VALUE, MODEL_SELECT_PLACEHOLDER, normalizeModelName } from './utils'; const MODEL_SELECT_PLACEHOLDER_VALUE = 'model_placeholder$$'; const PIPELINE_SELECT_PLACEHOLDER_VALUE = 'pipeline_placeholder$$'; -const CHOOSE_EXISTING_LABEL = i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.chooseLabel', - { defaultMessage: 'Choose' } -); -const CHOOSE_NEW_LABEL = i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.newLabel', - { defaultMessage: 'New pipeline' } +const CREATE_NEW_TAB_NAME = i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.tabs.createNew.name', + { defaultMessage: 'Create new' } ); -const CHOOSE_PIPELINE_LABEL = i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.existingLabel', - { defaultMessage: 'Existing pipeline' } + +const USE_EXISTING_TAB_NAME = i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.tabs.useExisting.name', + { defaultMessage: 'Use existing' } ); +export enum ConfigurePipelineTabId { + CREATE_NEW = 'create_new', + USE_EXISTING = 'use_existing', +} + export const ConfigurePipeline: React.FC = () => { const { addInferencePipelineModal: { configuration }, @@ -68,14 +62,7 @@ export const ConfigurePipeline: React.FC = () => { const { ingestionMethod } = useValues(IndexViewLogic); const { indexName } = useValues(IndexNameLogic); - const { existingPipeline, modelID, pipelineName } = configuration; - - useEffect(() => { - setInferencePipelineConfiguration({ - ...configuration, - pipelineName: pipelineName || indexName, - }); - }, []); + const { existingPipeline, modelID, pipelineName, isPipelineNameUserSupplied } = configuration; const nameError = formErrors.pipelineName !== undefined && pipelineName.length > 0; @@ -113,222 +100,163 @@ export const ConfigurePipeline: React.FC = () => { const inputsDisabled = configuration.existingPipeline !== false; - return ( - <> - - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.title', - { defaultMessage: 'Create or select a pipeline' } - )} -

    -
    + const tabs: EuiTabbedContentTab[] = [ + { + content: ( + <> - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.description', + + -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionUsePipelines', - { - defaultMessage: - 'Pipelines you create will be saved to be used elsewhere in your Elastic deployment.', - } - )} -

    -
    -
    - - - - - - setInferencePipelineConfiguration({ - ...EMPTY_PIPELINE_CONFIGURATION, - existingPipeline: e.target.value === 'true', - }) - } - value={configuration.existingPipeline?.toString() ?? ''} - /> - - {configuration.existingPipeline === true ? ( - - 0 ? pipelineName : PIPELINE_SELECT_PLACEHOLDER_VALUE - } - options={pipelineOptions} - onChange={(value) => selectExistingPipeline(value)} - /> - - ) : ( - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', - { - defaultMessage: - 'Pipeline names are unique within a deployment and can only contain letters, numbers, underscores, and hyphens. This will create a pipeline named {pipelineName}.', - values: { - pipelineName: `ml-inference-${pipelineName}`, - }, - } - )} - - ) - } - error={nameError && formErrors.pipelineName} - isInvalid={nameError} - > - + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', { - defaultMessage: 'Enter a unique name for this pipeline', + defaultMessage: + 'Pipeline names are unique within a deployment and can only contain letters, numbers, underscores, and hyphens. This will create a pipeline named {pipelineName}.', + values: { + pipelineName: `ml-inference-${pipelineName}`, + }, } )} - value={pipelineName} - onChange={(e) => - setInferencePipelineConfiguration({ - ...configuration, - pipelineName: e.target.value, - }) - } - /> - - )} - - - -
    - - - - -

    - {i18n.translate( + + ) + } + error={nameError && formErrors.pipelineName} + isInvalid={nameError} + > + + setInferencePipelineConfiguration({ + ...configuration, + isPipelineNameUserSupplied: e.target.value.length > 0, + pipelineName: e.target.value, + }) + } + /> + + - - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionDeployTrainedModel', - { - defaultMessage: - 'To perform natural language processing tasks in your cluster, you must deploy an appropriate trained model.', + > + + setInferencePipelineConfiguration({ + ...configuration, + inferenceConfig: undefined, + modelID: value, + fieldMappings: undefined, + pipelineName: isPipelineNameUserSupplied + ? pipelineName + : indexName + '-' + normalizeModelName(value), + }) } - )} -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink', + options={modelOptions} + valueOfSelected={modelID === '' ? MODEL_SELECT_PLACEHOLDER_VALUE : modelID} + /> +
    + + + ), + id: ConfigurePipelineTabId.CREATE_NEW, + name: CREATE_NEW_TAB_NAME, + }, + { + content: ( + <> + + + - - - - - - + - - setInferencePipelineConfiguration({ - ...configuration, - inferenceConfig: undefined, - modelID: value, - fieldMappings: undefined, - }) - } - options={modelOptions} - valueOfSelected={modelID === '' ? MODEL_SELECT_PLACEHOLDER_VALUE : modelID} - /> - - - - - - - - + hasDividers + data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureInferencePipeline-selectExistingPipeline`} + valueOfSelected={ + pipelineName.length > 0 ? pipelineName : PIPELINE_SELECT_PLACEHOLDER_VALUE + } + options={pipelineOptions} + onChange={(value) => selectExistingPipeline(value)} + /> + + + + ), + id: ConfigurePipelineTabId.USE_EXISTING, + name: USE_EXISTING_TAB_NAME, + }, + ]; + + return ( + <> + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.title', + { defaultMessage: 'Configure a pipeline' } + )} +

    +
    + + +

    + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.description', + { + defaultMessage: + 'Build or reuse a child pipeline that will be used as a processor in your main pipeline.', + } + )} +

    +
    + + { + const isExistingPipeline = tab.id === ConfigurePipelineTabId.USE_EXISTING; + if (isExistingPipeline !== configuration.existingPipeline) { + setInferencePipelineConfiguration({ + ...EMPTY_PIPELINE_CONFIGURATION, + existingPipeline: isExistingPipeline, + }); + } + }} + /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.test.tsx similarity index 97% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.test.tsx index 356de3acc9dc6..6e5e2cb3fbbb8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx similarity index 98% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx index ef7dd486e5eb1..594ec2c6b4510 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/deploy_model.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/deploy_model.tsx @@ -21,7 +21,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedHTMLMessage } from '@kbn/i18n-react'; -import { docLinks } from '../../../../../shared/doc_links'; +import { docLinks } from '../../../../../../shared/doc_links'; import { TextExpansionCallOutState, TextExpansionDismissButton } from './text_expansion_callout'; import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; @@ -55,7 +55,7 @@ export const DeployModel = ({

    {i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title', - { defaultMessage: 'Improve your results with ELSER v2' } + { defaultMessage: 'Improve your results with ELSER' } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.test.tsx similarity index 97% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.test.tsx index a17eae3ef75f6..84172ce34ccc5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.tsx similarity index 97% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.tsx index 50d8ea47fb8f1..fe8f0b7953c7d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployed.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployed.tsx @@ -51,7 +51,7 @@ export const ModelDeployed = ({

    {i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedTitle', - { defaultMessage: 'Your ELSER v2 model has deployed but not started.' } + { defaultMessage: 'Your ELSER model has deployed but not started.' } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.test.tsx similarity index 94% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.test.tsx index f147778539f55..e2493fad0e4c3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.tsx similarity index 96% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.tsx index 8804f4ec58439..f9b9439833255 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_deployment_in_progress.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_deployment_in_progress.tsx @@ -28,7 +28,7 @@ export const ModelDeploymentInProgress = ({

    {i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingTitle', - { defaultMessage: 'Your ELSER v2 model is deploying.' } + { defaultMessage: 'Your ELSER model is deploying.' } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.test.tsx similarity index 96% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.test.tsx index c98ca42a41121..e5bf09b61e412 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.tsx similarity index 88% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.tsx index fa5a46d438041..1a15b8e96ab36 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_started.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/model_started.tsx @@ -17,14 +17,15 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { KibanaLogic } from '../../../../../shared/kibana'; +import { KibanaLogic } from '../../../../../../shared/kibana'; + +import { TRAINED_MODELS_PATH } from '../utils'; import { TextExpansionCallOutState, TextExpansionDismissButton, FineTuneModelsButton, } from './text_expansion_callout'; -import { TRAINED_MODELS_PATH } from './utils'; export const ModelStarted = ({ dismiss, @@ -49,20 +50,20 @@ export const ModelStarted = ({ ? isCompact ? i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitleCompact', - { defaultMessage: 'Your ELSER v2 model is running single-threaded.' } + { defaultMessage: 'Your ELSER model is running single-threaded.' } ) : i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitle', - { defaultMessage: 'Your ELSER v2 model has started single-threaded.' } + { defaultMessage: 'Your ELSER model has started single-threaded.' } ) : isCompact ? i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitleCompact', - { defaultMessage: 'Your ELSER v2 model is running.' } + { defaultMessage: 'Your ELSER model is running.' } ) : i18n.translate( 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitle', - { defaultMessage: 'Your ELSER v2 model has started.' } + { defaultMessage: 'Your ELSER model has started.' } )}

    @@ -91,7 +92,7 @@ export const ModelStarted = ({ 'xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedBody', { defaultMessage: - 'Enjoy the power of ELSER v2 in your custom Inference pipeline.', + 'Enjoy the power of ELSER in your custom Inference pipeline.', } )}

    diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.test.tsx similarity index 97% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.test.tsx index 1ef7480b25c81..4abd583e4a79f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.tsx similarity index 95% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.tsx index 310f8f273a2cb..1ea2c63ccaaa3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout.tsx @@ -12,8 +12,10 @@ import { useValues } from 'kea'; import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { KibanaLogic } from '../../../../../shared/kibana'; -import { IndexViewLogic } from '../../index_view_logic'; +import { KibanaLogic } from '../../../../../../shared/kibana'; +import { IndexViewLogic } from '../../../index_view_logic'; + +import { TRAINED_MODELS_PATH } from '../utils'; import { DeployModel } from './deploy_model'; import { ModelDeployed } from './model_deployed'; @@ -22,7 +24,6 @@ import { ModelStarted } from './model_started'; import { useTextExpansionCallOutData } from './text_expansion_callout_data'; import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; import { TextExpansionErrors } from './text_expansion_errors'; -import { TRAINED_MODELS_PATH } from './utils'; export interface TextExpansionCallOutState { dismiss: () => void; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_data.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_data.tsx similarity index 96% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_data.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_data.tsx index 849bcd7964f76..4cddec9b7d2d6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_data.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useValues } from 'kea'; -import { IndexViewLogic } from '../../index_view_logic'; +import { IndexViewLogic } from '../../../index_view_logic'; import { TextExpansionCallOutProps, TextExpansionCallOutState } from './text_expansion_callout'; import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.test.ts similarity index 94% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.test.ts index e39230ee2b69b..1380e05e4c57f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.test.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { LogicMounter } from '../../../../../__mocks__/kea_logic'; +import { LogicMounter } from '../../../../../../__mocks__/kea_logic'; import { HttpResponse } from '@kbn/core/public'; -import { ErrorResponse, HttpError, Status } from '../../../../../../../common/types/api'; -import { MlModelDeploymentState } from '../../../../../../../common/types/ml'; -import { CreateTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic'; -import { FetchTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic'; -import { StartTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/start_text_expansion_model_api_logic'; +import { ErrorResponse, HttpError, Status } from '../../../../../../../../common/types/api'; +import { MlModelDeploymentState } from '../../../../../../../../common/types/ml'; +import { CreateTextExpansionModelApiLogic } from '../../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic'; +import { FetchTextExpansionModelApiLogic } from '../../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic'; +import { StartTextExpansionModelApiLogic } from '../../../../../api/ml_models/text_expansion/start_text_expansion_model_api_logic'; import { getTextExpansionError, @@ -80,19 +80,19 @@ describe('TextExpansionCalloutLogic', () => { }); it('uses the correct title and message from a create error', () => { expect(getTextExpansionError(error, undefined, undefined)).toEqual({ - title: 'Error with ELSER v2 deployment', + title: 'Error with ELSER deployment', message: error.body?.message, }); }); it('uses the correct title and message from a fetch error', () => { expect(getTextExpansionError(undefined, error, undefined)).toEqual({ - title: 'Error fetching ELSER v2 model', + title: 'Error fetching ELSER model', message: error.body?.message, }); }); it('uses the correct title and message from a start error', () => { expect(getTextExpansionError(undefined, undefined, error)).toEqual({ - title: 'Error starting ELSER v2 deployment', + title: 'Error starting ELSER deployment', message: error.body?.message, }); }); @@ -303,7 +303,7 @@ describe('TextExpansionCalloutLogic', () => { describe('textExpansionError', () => { const error = { body: { - error: 'Error with ELSER v2 deployment', + error: 'Error with ELSER deployment', message: 'Mocked error message', statusCode: 500, }, @@ -318,21 +318,21 @@ describe('TextExpansionCalloutLogic', () => { it('returns extracted error for create', () => { CreateTextExpansionModelApiLogic.actions.apiError(error); expect(TextExpansionCalloutLogic.values.textExpansionError).toStrictEqual({ - title: 'Error with ELSER v2 deployment', + title: 'Error with ELSER deployment', message: 'Mocked error message', }); }); it('returns extracted error for fetch', () => { FetchTextExpansionModelApiLogic.actions.apiError(error); expect(TextExpansionCalloutLogic.values.textExpansionError).toStrictEqual({ - title: 'Error fetching ELSER v2 model', + title: 'Error fetching ELSER model', message: 'Mocked error message', }); }); it('returns extracted error for start', () => { StartTextExpansionModelApiLogic.actions.apiError(error); expect(TextExpansionCalloutLogic.values.textExpansionError).toStrictEqual({ - title: 'Error starting ELSER v2 deployment', + title: 'Error starting ELSER deployment', message: 'Mocked error message', }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.ts similarity index 93% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.ts index e8e6913c38ce8..06d4f553bbabd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_callout_logic.ts @@ -9,26 +9,26 @@ import { kea, MakeLogicType } from 'kea'; import { i18n } from '@kbn/i18n'; -import { HttpError, Status } from '../../../../../../../common/types/api'; -import { MlModelDeploymentState } from '../../../../../../../common/types/ml'; -import { getErrorsFromHttpResponse } from '../../../../../shared/flash_messages/handle_api_errors'; +import { HttpError, Status } from '../../../../../../../../common/types/api'; +import { MlModelDeploymentState } from '../../../../../../../../common/types/ml'; +import { getErrorsFromHttpResponse } from '../../../../../../shared/flash_messages/handle_api_errors'; -import { KibanaLogic } from '../../../../../shared/kibana'; +import { KibanaLogic } from '../../../../../../shared/kibana'; import { CreateTextExpansionModelApiLogic, CreateTextExpansionModelApiLogicActions, CreateTextExpansionModelResponse, -} from '../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic'; +} from '../../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic'; import { FetchTextExpansionModelApiLogic, FetchTextExpansionModelApiLogicActions, FetchTextExpansionModelResponse, -} from '../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic'; +} from '../../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic'; import { StartTextExpansionModelApiLogic, StartTextExpansionModelApiLogicActions, -} from '../../../../api/ml_models/text_expansion/start_text_expansion_model_api_logic'; +} from '../../../../../api/ml_models/text_expansion/start_text_expansion_model_api_logic'; const FETCH_TEXT_EXPANSION_MODEL_POLLING_DURATION = 5000; // 5 seconds const FETCH_TEXT_EXPANSION_MODEL_POLLING_DURATION_ON_FAILURE = 30000; // 30 seconds @@ -97,7 +97,7 @@ export const getTextExpansionError = ( title: i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.title', { - defaultMessage: 'Error with ELSER v2 deployment', + defaultMessage: 'Error with ELSER deployment', } ), message: getErrorsFromHttpResponse(createError)[0], @@ -107,7 +107,7 @@ export const getTextExpansionError = ( title: i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionStartError.title', { - defaultMessage: 'Error starting ELSER v2 deployment', + defaultMessage: 'Error starting ELSER deployment', } ), message: getErrorsFromHttpResponse(startError)[0], @@ -117,7 +117,7 @@ export const getTextExpansionError = ( title: i18n.translate( 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionFetchError.title', { - defaultMessage: 'Error fetching ELSER v2 model', + defaultMessage: 'Error fetching ELSER model', } ), message: getErrorsFromHttpResponse(fetchError)[0], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.test.tsx similarity index 93% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.test.tsx index cb15c2c5c5411..2a7a6ea610bda 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; import React from 'react'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.tsx similarity index 84% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.tsx index a46105586af1e..e55f6109c177f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout/text_expansion_errors.tsx @@ -13,10 +13,10 @@ import { EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { HttpLogic } from '../../../../../shared/http'; +import { HttpLogic } from '../../../../../../shared/http'; -import { SendEnterpriseSearchTelemetry } from '../../../../../shared/telemetry'; -import { ML_NOTIFICATIONS_PATH } from '../../../../routes'; +import { SendEnterpriseSearchTelemetry } from '../../../../../../shared/telemetry'; +import { ML_NOTIFICATIONS_PATH } from '../../../../../routes'; export const TextExpansionErrors = ({ error }: { error: { title: string; message: string } }) => { const { http } = useValues(HttpLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts index a9402e7e966b8..3a645dcbba3b4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts @@ -12,6 +12,7 @@ import { InferencePipelineInferenceConfig } from '../../../../../../../common/ty export interface InferencePipelineConfiguration { existingPipeline?: boolean; inferenceConfig?: InferencePipelineInferenceConfig; + isPipelineNameUserSupplied?: boolean; modelID: string; pipelineName: string; fieldMappings?: FieldMapping[]; @@ -27,7 +28,6 @@ export interface AddInferencePipelineFormErrors { export enum AddInferencePipelineSteps { Configuration, Fields, - Mappings, Test, Review, } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.test.tsx deleted file mode 100644 index b615b7930130f..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.test.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { setMockValues } from '../../../../../__mocks__/kea_logic'; - -import React from 'react'; - -import { shallow } from 'enzyme'; - -import { EuiBasicTable } from '@elastic/eui'; - -import { - UpdateMappings, - UpdateMappingsAutomatic, - UpdateMappingsInstructions, -} from './update_mappings'; - -describe('UpdateMappings', () => { - const DEFAULT_VALUES = { - isTextExpansionModelSelected: false, - addInferencePipelineModal: { configuration: { existingPipeline: false } }, - }; - - beforeEach(() => { - jest.clearAllMocks(); - setMockValues(DEFAULT_VALUES); - }); - it('renders selected fields', () => { - const wrapper = shallow(); - expect(wrapper.find(EuiBasicTable)).toHaveLength(1); - }); - it('renders instructions if mappings need to be updated manually', () => { - const wrapper = shallow(); - expect(wrapper.find(UpdateMappingsInstructions)).toHaveLength(1); - }); - it('renders instructions when attaching existing pipeline', () => { - setMockValues({ - ...DEFAULT_VALUES, - addInferencePipelineModal: { configuration: { existingPipeline: true } }, - }); - const wrapper = shallow(); - expect(wrapper.find(UpdateMappingsInstructions)).toHaveLength(1); - }); - it('renders info panel if text expansion model is selected', () => { - setMockValues({ - ...DEFAULT_VALUES, - isTextExpansionModelSelected: true, - }); - const wrapper = shallow(); - expect(wrapper.find(UpdateMappingsAutomatic)).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.tsx deleted file mode 100644 index e8bf9f5cff5e1..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/update_mappings.tsx +++ /dev/null @@ -1,179 +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 React from 'react'; - -import { useValues } from 'kea'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiText, - EuiPanel, - EuiBasicTableColumn, - EuiBasicTable, - EuiCallOut, - EuiLink, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { FieldMapping } from '../../../../../../../common/ml_inference_pipeline'; - -import { docLinks } from '../../../../../shared/doc_links'; - -import { MLInferenceLogic } from './ml_inference_logic'; - -export const UpdateMappingsInstructions: React.FC = () => ( - - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.title', - { - defaultMessage: 'Update your index mappings', - } - )} -

    -
    -
    - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.description', - { - defaultMessage: - 'You must manually update your index mappings before you can start indexing documents through the pipeline.', - } - )} -

    -
    -
    - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.presentInMapping', - { - defaultMessage: - 'Make sure the selected inference output fields are present in the mapping.', - } - )} -

    -
    -
    - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.docsLink', - { - defaultMessage: 'Learn more', - } - )} - - -
    -); - -export const UpdateMappingsAutomatic: React.FC = () => ( - - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.titleNoAction', - { - defaultMessage: 'Review index mapping updates', - } - )} -

    -
    -
    - - -

    - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.descriptionNoAction', - { - defaultMessage: - 'Your index mappings will automatically be updated to include the selected inference output fields.', - } - )} -

    -
    -
    - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.docsLink', - { - defaultMessage: 'Learn more', - } - )} - - -
    -); - -export const UpdateMappings: React.FC = () => { - const { - addInferencePipelineModal: { configuration }, - isTextExpansionModelSelected, - } = useValues(MLInferenceLogic); - - const areMappingsAutoUpdated = isTextExpansionModelSelected && !configuration.existingPipeline; - - const columns: Array> = [ - { - field: 'targetField', - name: areMappingsAutoUpdated - ? i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappings', - { - defaultMessage: 'Field mappings', - } - ) - : i18n.translate( - 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappingsRequired', - { - defaultMessage: 'Required field mappings', - } - ), - }, - ]; - - return ( - <> - - - {areMappingsAutoUpdated ? : } - - - - - - - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.test.ts index fc39d2f429a25..4710564b695ac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isValidPipelineName } from './utils'; +import { isValidPipelineName, normalizeModelName } from './utils'; describe('ml inference utils', () => { describe('isValidPipelineName', () => { @@ -24,4 +24,16 @@ describe('ml inference utils', () => { expect(isValidPipelineName('a_pipeline_name_1"')).toEqual(false); }); }); + describe('normalizeModelName', () => { + it('no normalization', () => { + expect(normalizeModelName('valid_model_name_123')).toEqual('valid_model_name_123'); + expect(normalizeModelName('valid-model-name-123')).toEqual('valid-model-name-123'); + }); + it('normalize model name', () => { + expect(normalizeModelName('.elser_model_2')).toEqual('_elser_model_2'); + expect(normalizeModelName('model!@with#$special%^chars&*123')).toEqual( + 'model__with__special__chars__123' + ); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts index d05b1453547bd..02cf5a7463dde 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts @@ -12,6 +12,7 @@ import { FetchPipelineResponse } from '../../../../api/pipelines/fetch_pipeline' import { AddInferencePipelineFormErrors, InferencePipelineConfiguration } from './types'; const VALID_PIPELINE_NAME_REGEX = /^[\w\-]+$/; +const NORMALIZABLE_PIPELINE_CHARS_REGEX = /[^\w\-]/g; export const TRAINED_MODELS_PATH = '/app/ml/trained_models'; export const isValidPipelineName = (input: string): boolean => { @@ -79,6 +80,10 @@ export const validateInferencePipelineFields = ( return errors; }; +export const normalizeModelName = (modelName: string): string => { + return modelName.replace(NORMALIZABLE_PIPELINE_CHARS_REGEX, '_'); +}; + export const EXISTING_PIPELINE_DISABLED_MISSING_SOURCE_FIELDS = ( commaSeparatedMissingSourceFields: string ) => diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference_pipeline_processors_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference_pipeline_processors_card.tsx index 6d3ad46a985c0..718138535afba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference_pipeline_processors_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference_pipeline_processors_card.tsx @@ -18,7 +18,7 @@ import { IndexNameLogic } from '../index_name_logic'; import { InferencePipelineCard } from './inference_pipeline_card'; import { AddMLInferencePipelineButton } from './ml_inference/add_ml_inference_button'; -import { TextExpansionCallOut } from './ml_inference/text_expansion_callout'; +import { TextExpansionCallOut } from './ml_inference/text_expansion_callout/text_expansion_callout'; import { PipelinesLogic } from './pipelines_logic'; export const MlInferencePipelineProcessorsCard: React.FC = () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx index c29790700c438..f9f38e7c13332 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.test.tsx @@ -14,8 +14,6 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; - import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; import { GettingStartedSteps } from '../../../shared/getting_started_steps'; @@ -75,20 +73,6 @@ describe('SearchIndices', () => { expect(wrapper.find(ElasticsearchResources)).toHaveLength(0); expect(mockActions.fetchIndices).toHaveBeenCalled(); - expect(wrapper.find(EuiCallOut)).toHaveLength(1); - }); - - it('dismisses callout on click to button', () => { - setMockValues(mockValues); - setMockActions(mockActions); - - const wrapper = shallow(); - const dismissButton = wrapper.find(EuiCallOut).find(EuiButton); - expect(global.localStorage.getItem('enterprise-search-indices-callout-dismissed')).toBe( - 'false' - ); - dismissButton.simulate('click'); - expect(global.localStorage.getItem('enterprise-search-indices-callout-dismissed')).toBe('true'); }); // Move this test to the indices table when writing tests there diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx index 8e778228857e1..beb91be468011 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx @@ -18,7 +18,6 @@ import { EuiTitle, EuiSwitch, EuiSearchBar, - EuiLink, EuiToolTip, EuiCode, } from '@elastic/eui'; @@ -27,14 +26,12 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; -import { docLinks } from '../../../shared/doc_links'; import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; import { GettingStartedSteps } from '../../../shared/getting_started_steps'; import { HttpLogic } from '../../../shared/http/http_logic'; import { KibanaLogic } from '../../../shared/kibana'; import { EuiButtonTo, EuiLinkTo } from '../../../shared/react_router_helpers'; import { handlePageChange } from '../../../shared/table_pagination'; -import { useLocalStorage } from '../../../shared/use_local_storage'; import { NEW_INDEX_PATH } from '../../routes'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; @@ -62,11 +59,6 @@ export const SearchIndices: React.FC = () => { const { config } = useValues(KibanaLogic); const { errorConnectingMessage } = useValues(HttpLogic); - const [calloutDismissed, setCalloutDismissed] = useLocalStorage( - 'enterprise-search-indices-callout-dismissed', - false - ); - useEffect(() => { // We don't want to trigger loading for each search query change, so we need this // flag to set if the call to backend is first request. @@ -162,46 +154,6 @@ export const SearchIndices: React.FC = () => { )} {!hasNoIndices ? ( - {!calloutDismissed && ( - - - -

    - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.callout.docLink', - { - defaultMessage: 'read the documentation', - } - )} - - ), - }} - /> -

    - setCalloutDismissed(true)}> - {i18n.translate('xpack.enterpriseSearch.content.callout.dismissButton', { - defaultMessage: 'Dismiss', - })} - -
    -
    - )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ml_inference/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ml_inference/utils.ts index c635e6c785319..3d97f52c659c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ml_inference/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ml_inference/utils.ts @@ -28,7 +28,7 @@ export const NLP_DISPLAY_TITLES: Record = { question_answering: i18n.translate( 'xpack.enterpriseSearch.content.ml_inference.question_answering', { - defaultMessage: 'Named Entity Recognition', + defaultMessage: 'Question Answering', } ), text_classification: i18n.translate( @@ -41,7 +41,7 @@ export const NLP_DISPLAY_TITLES: Record = { defaultMessage: 'Dense Vector Text Embedding', }), text_expansion: i18n.translate('xpack.enterpriseSearch.content.ml_inference.text_expansion', { - defaultMessage: 'ELSER Text Expansion', + defaultMessage: 'Elastic Learned Sparse EncodeR (ELSER)', }), zero_shot_classification: i18n.translate( 'xpack.enterpriseSearch.content.ml_inference.zero_shot_classification', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx index abb569361e7c3..26d826d7b8fb0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx @@ -11,18 +11,33 @@ import { generatePath } from 'react-router-dom'; import { useValues } from 'kea'; -import { EuiButton, EuiCard, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { + EuiButton, + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSpacer, + EuiText, + IconType, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ENTERPRISE_SEARCH_CONTENT_PLUGIN, + ENTERPRISE_SEARCH_ELASTICSEARCH_URL, INGESTION_METHOD_IDS, } from '../../../../../common/constants'; import apiLogo from '../../../../assets/images/api_cloud.svg'; +import connectorIcon from '../../../../assets/images/connector.svg'; +import crawlerIcon from '../../../../assets/images/crawler.svg'; +import fileUploadLogo from '../../../../assets/images/file_upload_logo.svg'; +import sampleDataLogo from '../../../../assets/images/sample_data_logo.svg'; import connectorLogo from '../../../../assets/images/search_connector.svg'; import crawlerLogo from '../../../../assets/images/search_crawler.svg'; +import languageClientsLogo from '../../../../assets/images/search_language_clients.svg'; import { NEW_API_PATH, @@ -31,104 +46,229 @@ import { } from '../../../enterprise_search_content/routes'; import { HttpLogic } from '../../../shared/http/http_logic'; import { KibanaLogic } from '../../../shared/kibana'; -import { EuiButtonTo, EuiLinkTo } from '../../../shared/react_router_helpers'; - -const START_LABEL = i18n.translate('xpack.enterpriseSearch.ingestSelector.startButton', { - defaultMessage: 'Start', -}); +import { EuiLinkTo } from '../../../shared/react_router_helpers'; export const IngestionSelector: React.FC = () => { - const { config, productFeatures } = useValues(KibanaLogic); + const { + application: { navigateToApp }, + config, + productFeatures, + } = useValues(KibanaLogic); const { errorConnectingMessage } = useValues(HttpLogic); const crawlerDisabled = Boolean(errorConnectingMessage || !config.host); return ( - - - } - textAlign="left" - title={i18n.translate('xpack.enterpriseSearch.ingestSelector.method.api', { - defaultMessage: 'API', - })} - description={i18n.translate( - 'xpack.enterpriseSearch.ingestSelector.method.api.description', - { - defaultMessage: - 'Add documents programmatically by connecting with the API using your preferred language client.', - } - )} - footer={ - - {START_LABEL} - - } - /> - - {productFeatures.hasConnectors && ( + <> + - } - textAlign="left" - title={i18n.translate('xpack.enterpriseSearch.ingestSelector.method.connectors', { - defaultMessage: 'Connectors', + - {START_LABEL} - - } /> - )} - {productFeatures.hasWebCrawler && ( + {productFeatures.hasConnectors && ( + + + + )} + {productFeatures.hasWebCrawler && ( + + + + )} + + + - } - textAlign="left" - title={i18n.translate('xpack.enterpriseSearch.ingestSelector.method.crawler', { - defaultMessage: 'Web Crawler', + - {START_LABEL} - - } + buttonIcon="visVega" + buttonLabel={i18n.translate( + 'xpack.enterpriseSearch.ingestSelector.method.browseClientsLabel', + { + defaultMessage: 'Browse clients', + } + )} + href={generatePath(ENTERPRISE_SEARCH_ELASTICSEARCH_URL)} /> - )} - + + navigateToApp('home', { path: '#/tutorial_directory/fileDataViz' })} + /> + + + navigateToApp('home', { path: '#/tutorial_directory/sampleData' })} + /> + + + + ); +}; + +interface IngestionCardProps { + buttonIcon: IconType; + buttonLabel: string; + description: string; + href?: string; + isDisabled?: boolean; + logo: IconType; + onClick?: () => void; + title: string; +} + +const IngestionCard: React.FC = ({ + buttonIcon, + buttonLabel, + description, + href, + isDisabled, + logo, + onClick, + title, +}) => { + return ( + + + + + + {title} + + + + } + description={ + + {description} + + } + footer={ + onClick ? ( + + {buttonLabel} + + ) : ( + + + {buttonLabel} + + + ) + } + /> ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.scss b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.scss index 8d02868008375..b21b889138b16 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.scss +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.scss @@ -1,7 +1,3 @@ -.entSearchProductSelectorHeader { - background-color: $euiColorPrimary; -} - .entSearchProductSelectorHeader > div { padding-bottom: 0; padding-top: 0; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx index 13b84c9c6e40b..cd869c5444b37 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx @@ -18,7 +18,6 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { WelcomeBanner } from '@kbn/search-api-panels'; import { AuthenticatedUser } from '@kbn/security-plugin/common'; @@ -39,6 +38,7 @@ import { EnterpriseSearchProductCard } from './enterprise_search_product_card'; import { IngestionSelector } from './ingestion_selector'; import './product_selector.scss'; +import { WelcomeBanner } from './welcome_banner'; interface ProductSelectorProps { access: { @@ -81,9 +81,7 @@ export const ProductSelector: React.FC = ({ - - - + diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx new file mode 100644 index 0000000000000..ea14d118d4c6e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiText, EuiImage } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { AuthenticatedUser } from '@kbn/security-plugin/public'; + +export interface WelcomeBannerProps { + assetBasePath?: string; + image?: string; + user?: AuthenticatedUser; +} + +export const WelcomeBanner: React.FC = ({ user, assetBasePath, image }) => ( + <> + + + + {/* Reversing column direction here so screenreaders keep h1 as the first element */} + + + +

    + +

    +
    + + + {i18n.translate('xpack.enterpriseSearch.welcomeBanner.header.titleDescription', { + defaultMessage: + "There's endless ways to ingest and explore data with Elasticsearch, but here's a few of the most popular", + })} + +
    + {Boolean(user) && ( + + +

    + {user + ? i18n.translate( + 'xpack.enterpriseSearch.welcomeBanner.header.greeting.customTitle', + { + defaultMessage: '👋 Hi {name}!', + values: { name: user.full_name || user.username }, + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.welcomeBanner.header.greeting.defaultTitle', + { + defaultMessage: '👋 Hi', + } + )} +

    +
    +
    + )} +
    +
    + + + +
    + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index 82a4eedcd9c34..5ba7146848727 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -119,7 +119,6 @@ class DocLinks { public licenseManagement: string; public machineLearningStart: string; public mlDocumentEnrichment: string; - public mlDocumentEnrichmentUpdateMappings: string; public pluginsIngestAttachment: string; public queryDsl: string; public restApis: string; @@ -290,7 +289,6 @@ class DocLinks { this.licenseManagement = ''; this.machineLearningStart = ''; this.mlDocumentEnrichment = ''; - this.mlDocumentEnrichmentUpdateMappings = ''; this.pluginsIngestAttachment = ''; this.queryDsl = ''; this.restApis = ''; @@ -463,8 +461,6 @@ class DocLinks { this.licenseManagement = docLinks.links.enterpriseSearch.licenseManagement; this.machineLearningStart = docLinks.links.enterpriseSearch.machineLearningStart; this.mlDocumentEnrichment = docLinks.links.enterpriseSearch.mlDocumentEnrichment; - this.mlDocumentEnrichmentUpdateMappings = - docLinks.links.enterpriseSearch.mlDocumentEnrichmentUpdateMappings; this.pluginsIngestAttachment = docLinks.links.plugins.ingestAttachment; this.queryDsl = docLinks.links.query.queryDsl; this.restApis = docLinks.links.apis.restApis; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index f68a2eee1c5af..5caeba721f367 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -26,6 +26,91 @@ const DEFAULT_PRODUCT_ACCESS: ProductAccess = { hasAppSearchAccess: true, hasWorkplaceSearchAccess: true, }; +const baseNavItems = [ + expect.objectContaining({ + href: '/app/enterprise_search/overview', + id: 'home', + items: undefined, + }), + { + id: 'content', + items: [ + { + href: '/app/enterprise_search/content/search_indices', + id: 'search_indices', + items: undefined, + name: 'Indices', + }, + { + href: '/app/enterprise_search/content/settings', + id: 'settings', + items: undefined, + name: 'Settings', + }, + ], + name: 'Content', + }, + { + id: 'applications', + items: [ + { + href: '/app/enterprise_search/applications/search_applications', + id: 'searchApplications', + items: undefined, + name: 'Search Applications', + }, + { + href: '/app/enterprise_search/analytics', + id: 'analyticsCollections', + items: undefined, + name: 'Behavioral Analytics', + }, + ], + name: 'Applications', + }, + { + id: 'es_getting_started', + items: [ + { + href: '/app/enterprise_search/elasticsearch', + id: 'elasticsearch', + items: undefined, + name: 'Elasticsearch', + }, + { + href: '/app/enterprise_search/vector_search', + id: 'vectorSearch', + items: undefined, + name: 'Vector Search', + }, + { + href: '/app/enterprise_search/ai_search', + id: 'aiSearch', + items: undefined, + name: 'AI Search', + }, + ], + name: 'Getting started', + }, + { + id: 'enterpriseSearch', + items: [ + { + href: '/app/enterprise_search/app_search', + id: 'app_search', + items: undefined, + name: 'App Search', + }, + { + href: '/app/enterprise_search/workplace_search', + id: 'workplace_search', + items: undefined, + name: 'Workplace Search', + }, + ], + name: 'Enterprise Search', + }, +]; describe('useEnterpriseSearchContentNav', () => { beforeEach(() => { @@ -41,79 +126,7 @@ describe('useEnterpriseSearchContentNav', () => { productFeatures: DEFAULT_PRODUCT_FEATURES, }); - expect(useEnterpriseSearchNav()).toEqual([ - { - id: 'content', - items: [ - { - href: '/app/enterprise_search/content/search_indices', - id: 'search_indices', - name: 'Indices', - }, - { - href: '/app/enterprise_search/content/settings', - id: 'settings', - items: undefined, - name: 'Settings', - }, - ], - name: 'Content', - }, - { - id: 'applications', - items: [ - { - href: '/app/enterprise_search/applications/search_applications', - id: 'searchApplications', - name: 'Search Applications', - }, - { - href: '/app/enterprise_search/analytics', - id: 'analyticsCollections', - name: 'Behavioral Analytics', - }, - ], - name: 'Applications', - }, - { - href: '/app/enterprise_search/overview', - id: 'es_getting_started', - items: [ - { - href: '/app/enterprise_search/elasticsearch', - id: 'elasticsearch', - name: 'Elasticsearch', - }, - { - href: '/app/enterprise_search/vector_search', - id: 'vectorSearch', - name: 'Vector Search', - }, - { - href: '/app/enterprise_search/ai_search', - id: 'aiSearch', - name: 'AI Search', - }, - ], - name: 'Getting started', - }, - { - id: 'enterpriseSearch', - items: [ - { - href: '/app/enterprise_search/app_search', - id: 'app_search', - name: 'App Search', - }, - { - href: '/app/enterprise_search/workplace_search', - id: 'workplace_search', - name: 'Workplace Search', - }, - ], - name: 'Enterprise Search', - }, - ]); + expect(useEnterpriseSearchNav()).toEqual(baseNavItems); }); it('excludes legacy products when the user has no access to them', () => { @@ -205,84 +218,14 @@ describe('useEnterpriseSearchApplicationNav', () => { }); it('returns an array of top-level Enterprise Search nav items', () => { - expect(useEnterpriseSearchApplicationNav()).toEqual([ - { - id: 'content', - items: [ - { - href: '/app/enterprise_search/content/search_indices', - id: 'search_indices', - name: 'Indices', - }, - { - href: '/app/enterprise_search/content/settings', - id: 'settings', - name: 'Settings', - }, - ], - name: 'Content', - }, - { - id: 'applications', - items: [ - { - href: '/app/enterprise_search/applications/search_applications', - id: 'searchApplications', - name: 'Search Applications', - }, - { - href: '/app/enterprise_search/analytics', - id: 'analyticsCollections', - name: 'Behavioral Analytics', - }, - ], - name: 'Applications', - }, - { - href: '/app/enterprise_search/overview', - id: 'es_getting_started', - items: [ - { - href: '/app/enterprise_search/elasticsearch', - id: 'elasticsearch', - name: 'Elasticsearch', - }, - { - href: '/app/enterprise_search/vector_search', - id: 'vectorSearch', - name: 'Vector Search', - }, - { - href: '/app/enterprise_search/ai_search', - id: 'aiSearch', - name: 'AI Search', - }, - ], - name: 'Getting started', - }, - { - id: 'enterpriseSearch', - items: [ - { - href: '/app/enterprise_search/app_search', - id: 'app_search', - name: 'App Search', - }, - { - href: '/app/enterprise_search/workplace_search', - id: 'workplace_search', - name: 'Workplace Search', - }, - ], - name: 'Enterprise Search', - }, - ]); + expect(useEnterpriseSearchApplicationNav()).toEqual(baseNavItems); }); it('returns selected engine sub nav items', () => { const engineName = 'my-test-engine'; const navItems = useEnterpriseSearchApplicationNav(engineName); - expect(navItems?.map((ni) => ni.name)).toEqual([ + expect(navItems![0].id).toEqual('home'); + expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([ 'Content', 'Applications', 'Getting started', @@ -338,7 +281,8 @@ describe('useEnterpriseSearchApplicationNav', () => { it('returns selected engine without tabs when isEmpty', () => { const engineName = 'my-test-engine'; const navItems = useEnterpriseSearchApplicationNav(engineName, true); - expect(navItems?.map((ni) => ni.name)).toEqual([ + expect(navItems![0].id).toEqual('home'); + expect(navItems?.slice(1).map((ni) => ni.name)).toEqual([ 'Content', 'Applications', 'Getting started', @@ -397,74 +341,6 @@ describe('useEnterpriseSearchApplicationNav', () => { }); describe('useEnterpriseSearchAnalyticsNav', () => { - const baseNavs = [ - { - id: 'content', - items: [ - { - href: '/app/enterprise_search/content/search_indices', - id: 'search_indices', - name: 'Indices', - }, - ], - name: 'Content', - }, - { - id: 'applications', - items: [ - { - href: '/app/enterprise_search/applications/search_applications', - id: 'searchApplications', - name: 'Search Applications', - }, - { - href: '/app/enterprise_search/analytics', - id: 'analyticsCollections', - name: 'Behavioral Analytics', - }, - ], - name: 'Applications', - }, - { - href: '/app/enterprise_search/overview', - id: 'es_getting_started', - items: [ - { - href: '/app/enterprise_search/elasticsearch', - id: 'elasticsearch', - name: 'Elasticsearch', - }, - { - href: '/app/enterprise_search/vector_search', - id: 'vectorSearch', - name: 'Vector Search', - }, - { - href: '/app/enterprise_search/ai_search', - id: 'aiSearch', - name: 'AI Search', - }, - ], - name: 'Getting started', - }, - { - id: 'enterpriseSearch', - items: [ - { - href: '/app/enterprise_search/app_search', - id: 'app_search', - name: 'App Search', - }, - { - href: '/app/enterprise_search/workplace_search', - id: 'workplace_search', - name: 'Workplace Search', - }, - ], - name: 'Enterprise Search', - }, - ]; - beforeEach(() => { jest.clearAllMocks(); setMockValues({ @@ -474,12 +350,36 @@ describe('useEnterpriseSearchAnalyticsNav', () => { it('returns basic nav all params are empty', () => { const navItems = useEnterpriseSearchAnalyticsNav(); - expect(navItems).toEqual(baseNavs); + // filter out settings item because we're setting hasDefaultIngestPipeline to false + expect(navItems).toEqual( + baseNavItems.map((item) => + item.id === 'content' + ? { + ...item, + items: item.items?.filter( + (contentItem: { id: string }) => contentItem.id !== 'settings' + ), + } + : item + ) + ); }); it('returns basic nav if only name provided', () => { + // filter out settings item because we're setting hasDefaultIngestPipeline to false const navItems = useEnterpriseSearchAnalyticsNav('my-test-collection'); - expect(navItems).toEqual(baseNavs); + expect(navItems).toEqual( + baseNavItems.map((item) => + item.id === 'content' + ? { + ...item, + items: item.items?.filter( + (contentItem: { id: string }) => contentItem.id !== 'settings' + ), + } + : item + ) + ); }); it('returns nav with sub items when name and paths provided', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index 8fe9c36766c60..dd41511a436a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiFlexGroup, EuiIcon, EuiSideNavItemType } from '@elastic/eui'; +import { EuiFlexGroup, EuiIcon, EuiSideNavItemType, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { @@ -34,6 +34,21 @@ export const useEnterpriseSearchNav = () => { if (!isSidebarEnabled) return undefined; const navItems: Array> = [ + { + id: 'home', + name: ( + + {i18n.translate('xpack.enterpriseSearch.nav.homeTitle', { + defaultMessage: 'Home', + })} + + ), + ...generateNavLink({ + shouldNotCreateHref: true, + shouldShowActiveForSubroutes: true, + to: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL, + }), + }, { id: 'content', items: [ @@ -98,13 +113,6 @@ export const useEnterpriseSearchNav = () => { }, { id: 'es_getting_started', - name: i18n.translate('xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle', { - defaultMessage: 'Getting started', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - to: ENTERPRISE_SEARCH_OVERVIEW_PLUGIN.URL, - }), items: [ { id: 'elasticsearch', @@ -135,6 +143,9 @@ export const useEnterpriseSearchNav = () => { }), }, ], + name: i18n.translate('xpack.enterpriseSearch.nav.enterpriseSearchOverviewTitle', { + defaultMessage: 'Getting started', + }), }, ...(productAccess.hasAppSearchAccess || productAccess.hasWorkplaceSearchAccess ? [ diff --git a/x-pack/plugins/enterprise_search/public/assets/images/connector.svg b/x-pack/plugins/enterprise_search/public/assets/images/connector.svg new file mode 100644 index 0000000000000..3b37b963f435f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/connector.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/crawler.svg b/x-pack/plugins/enterprise_search/public/assets/images/crawler.svg new file mode 100644 index 0000000000000..94aafafddf68b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/crawler.svg @@ -0,0 +1,4 @@ + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/file_upload_logo.svg b/x-pack/plugins/enterprise_search/public/assets/images/file_upload_logo.svg new file mode 100644 index 0000000000000..90bbc987154ac --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/file_upload_logo.svg @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/sample_data_logo.svg b/x-pack/plugins/enterprise_search/public/assets/images/sample_data_logo.svg new file mode 100644 index 0000000000000..07e0d8fe5818a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/sample_data_logo.svg @@ -0,0 +1,407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/search_language_clients.svg b/x-pack/plugins/enterprise_search/public/assets/images/search_language_clients.svg new file mode 100644 index 0000000000000..c4ff851bc70ea --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/search_language_clients.svg @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index 978896289763f..6c5d44b00628d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -345,7 +345,7 @@ describe('startSync lib function', () => { }, filtering: null, id: 'connectorId', - index_name: `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}index_name`, + index_name: `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}search-index_name`, language: null, pipeline: null, service_type: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index ab58fd1417b73..634d7e97de5ec 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -23,7 +23,6 @@ import { } from '../../../common/constants'; import { ErrorCode } from '../../../common/types/error_codes'; -import { stripSearchPrefix } from '../../../common/utils/strip_search_prefix'; export const startSync = async ( client: IScopedClusterClient, @@ -70,10 +69,9 @@ export const startSync = async ( }); } - const indexNameWithoutSearchPrefix = index_name ? stripSearchPrefix(index_name) : ''; const targetIndexName = jobType === SyncJobType.ACCESS_CONTROL - ? `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexNameWithoutSearchPrefix}` + ? `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${index_name}` : index_name ?? undefined; return await startConnectorSync(client.asCurrentUser, { diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.test.ts index 0439c12c203db..fc14896e23c58 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.test.ts @@ -32,7 +32,7 @@ describe('deleteAccessControlIndex lib function', () => { deleteAccessControlIndex(mockClient as unknown as IScopedClusterClient, 'indexName') ).resolves.toEqual(true); expect(mockClient.asCurrentUser.indices.delete).toHaveBeenCalledWith({ - index: 'indexName', + index: '.search-acl-filter-indexName', }); }); }); @@ -58,7 +58,7 @@ describe('deleteAccessControlIndex lib function', () => { deleteAccessControlIndex(mockClient as unknown as IScopedClusterClient, 'indexName') ).resolves.not.toThrowError(); expect(mockClient.asCurrentUser.indices.delete).toHaveBeenCalledWith({ - index: 'indexName', + index: '.search-acl-filter-indexName', }); }); }); @@ -84,7 +84,7 @@ describe('deleteAccessControlIndex lib function', () => { deleteAccessControlIndex(mockClient as unknown as IScopedClusterClient, 'indexName') ).rejects.toEqual(mockErrorRejection); expect(mockClient.asCurrentUser.indices.delete).toHaveBeenCalledWith({ - index: 'indexName', + index: '.search-acl-filter-indexName', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts index 1937df11b912c..19769f09fa244 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/delete_access_control_index.ts @@ -8,14 +8,15 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX } from '../../../common/constants'; -import { stripSearchPrefix } from '../../../common/utils/strip_search_prefix'; import { isIndexNotFoundException } from '../../utils/identify_exceptions'; -export const deleteAccessControlIndex = async (client: IScopedClusterClient, index: string) => { +export const deleteAccessControlIndex = async (client: IScopedClusterClient, indexName: string) => { + const aclIndexName = `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexName}`; + try { return await client.asCurrentUser.indices.delete({ - index: stripSearchPrefix(index, CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX), + index: aclIndexName, }); } catch (e) { // Gracefully exit if index not found. This is a valid case. diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts index a28450108290a..92c87b354a470 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts @@ -94,7 +94,7 @@ describe('generateApiKey lib function', () => { cluster: ['monitor'], index: [ { - names: ['search-test', '.search-acl-filter-test', `${CONNECTORS_INDEX}*`], + names: ['search-test', '.search-acl-filter-search-test', `${CONNECTORS_INDEX}*`], privileges: ['all'], }, ], diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts index d256bc6a91d88..fb2ddbaad9f9d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts @@ -7,13 +7,16 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { ConnectorDocument, CONNECTORS_INDEX } from '@kbn/search-connectors'; +import { + ConnectorDocument, + CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, + CONNECTORS_INDEX, +} from '@kbn/search-connectors'; import { toAlphanumeric } from '../../../common/utils/to_alphanumeric'; export const generateApiKey = async (client: IScopedClusterClient, indexName: string) => { - // removes the "search-" prefix if present, and applies the new prefix - const aclIndexName = indexName.replace(/^(?:search-)?(.*)$/, '.search-acl-filter-$1'); + const aclIndexName = `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexName}`; const apiKeyResult = await client.asCurrentUser.security.createApiKey({ name: `${indexName}-connector`, diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts new file mode 100644 index 0000000000000..629be15a0bf3d --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts @@ -0,0 +1,309 @@ +/* + * Copyright 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 { MlTrainedModels } from '@kbn/ml-plugin/server'; + +import { MlModelDeploymentState } from '../../../common/types/ml'; + +import { fetchMlModels } from './fetch_ml_models'; +import { E5_MODEL_ID, ELSER_MODEL_ID } from './utils'; + +describe('fetchMlModels', () => { + const mockTrainedModelsProvider = { + getTrainedModels: jest.fn(), + getTrainedModelsStats: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('errors when there is no trained model provider', () => { + expect(() => fetchMlModels(undefined)).rejects.toThrowError('Machine Learning is not enabled'); + }); + + it('returns placeholders if no model is found', async () => { + const mockModelConfigs = { + count: 0, + trained_model_configs: [], + }; + + mockTrainedModelsProvider.getTrainedModels.mockImplementation(() => + Promise.resolve(mockModelConfigs) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(2); + expect(models[0]).toMatchObject({ + modelId: ELSER_MODEL_ID, + isPlaceholder: true, + deploymentState: MlModelDeploymentState.NotDeployed, + }); + expect(models[1]).toMatchObject({ + modelId: E5_MODEL_ID, + isPlaceholder: true, + deploymentState: MlModelDeploymentState.NotDeployed, + }); + expect(mockTrainedModelsProvider.getTrainedModelsStats).not.toHaveBeenCalled(); + }); + + it('combines existing models with placeholders', async () => { + const mockModelConfigs = { + count: 2, + trained_model_configs: [ + { + model_id: E5_MODEL_ID, + inference_config: { + text_embedding: {}, + }, + }, + { + model_id: 'model_1', + inference_config: { + text_classification: {}, + }, + }, + ], + }; + const mockModelStats = { + trained_model_stats: [ + { + model_id: E5_MODEL_ID, + }, + { + model_id: 'model_1', + }, + ], + }; + + mockTrainedModelsProvider.getTrainedModels.mockImplementation(() => + Promise.resolve(mockModelConfigs) + ); + mockTrainedModelsProvider.getTrainedModelsStats.mockImplementation(() => + Promise.resolve(mockModelStats) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(3); + expect(models[0].modelId).toEqual(ELSER_MODEL_ID); // Placeholder + expect(models[1]).toMatchObject({ + modelId: E5_MODEL_ID, + isPlaceholder: false, + }); + expect(models[2]).toMatchObject({ + modelId: 'model_1', + isPlaceholder: false, + }); + }); + + it('filters non-supported models', async () => { + const mockModelConfigs = { + count: 2, + trained_model_configs: [ + { + model_id: 'model_1', + inference_config: { + not_supported_1: {}, + }, + }, + { + model_id: 'model_2', + inference_config: { + not_supported_2: {}, + }, + }, + ], + }; + const mockModelStats = { + trained_model_stats: mockModelConfigs.trained_model_configs.map((modelConfig) => ({ + model_id: modelConfig.model_id, + })), + }; + + mockTrainedModelsProvider.getTrainedModels.mockImplementation(() => + Promise.resolve(mockModelConfigs) + ); + mockTrainedModelsProvider.getTrainedModelsStats.mockImplementation(() => + Promise.resolve(mockModelStats) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(2); + expect(models[0].modelId).toEqual(ELSER_MODEL_ID); // Placeholder + expect(models[1].modelId).toEqual(E5_MODEL_ID); // Placeholder + }); + + it('sets deployment state on models', async () => { + const mockModelConfigs = { + count: 3, + trained_model_configs: [ + { + model_id: ELSER_MODEL_ID, + inference_config: { + text_expansion: {}, + }, + }, + { + model_id: E5_MODEL_ID, + inference_config: { + text_embedding: {}, + }, + }, + { + model_id: 'model_1', + inference_config: { + ner: {}, + }, + }, + ], + }; + const mockModelStats = { + trained_model_stats: [ + { + model_id: ELSER_MODEL_ID, + deployment_stats: { + allocation_status: { + state: 'fully_allocated', + }, + }, + }, + { + model_id: E5_MODEL_ID, + deployment_stats: { + allocation_status: { + state: 'started', + }, + }, + }, + { + model_id: 'model_1', // No deployment_stats -> not deployed + }, + ], + }; + + mockTrainedModelsProvider.getTrainedModels.mockImplementation(() => + Promise.resolve(mockModelConfigs) + ); + mockTrainedModelsProvider.getTrainedModelsStats.mockImplementation(() => + Promise.resolve(mockModelStats) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(3); + expect(models[0]).toMatchObject({ + modelId: ELSER_MODEL_ID, + deploymentState: MlModelDeploymentState.FullyAllocated, + }); + expect(models[1]).toMatchObject({ + modelId: E5_MODEL_ID, + deploymentState: MlModelDeploymentState.Started, + }); + expect(models[2]).toMatchObject({ + modelId: 'model_1', + deploymentState: MlModelDeploymentState.NotDeployed, + }); + }); + + it('determines downloading/downloaded deployment state for promoted models', async () => { + const mockModelConfigs = { + count: 1, + trained_model_configs: [ + { + model_id: ELSER_MODEL_ID, + inference_config: { + text_expansion: {}, + }, + }, + ], + }; + const mockModelConfigsWithDefinition = { + count: 1, + trained_model_configs: [ + { + ...mockModelConfigs.trained_model_configs[0], + fully_defined: true, + }, + ], + }; + const mockModelStats = { + trained_model_stats: [ + { + model_id: ELSER_MODEL_ID, // No deployment_stats -> not deployed + }, + ], + }; + + // 1st call: get models + // 2nd call: get definition_status for ELSER + mockTrainedModelsProvider.getTrainedModels + .mockImplementationOnce(() => Promise.resolve(mockModelConfigs)) + .mockImplementationOnce(() => Promise.resolve(mockModelConfigsWithDefinition)); + mockTrainedModelsProvider.getTrainedModelsStats.mockImplementation(() => + Promise.resolve(mockModelStats) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(2); + expect(models[0]).toMatchObject({ + modelId: ELSER_MODEL_ID, + deploymentState: MlModelDeploymentState.Downloaded, + }); + expect(mockTrainedModelsProvider.getTrainedModels).toHaveBeenCalledTimes(2); + }); + + it('pins promoted models on top and sorts others by title', async () => { + const mockModelConfigs = { + count: 3, + trained_model_configs: [ + { + model_id: 'model_1', + inference_config: { + ner: {}, // "Named Entity Recognition" + }, + }, + { + model_id: 'model_2', + inference_config: { + text_embedding: {}, // "Dense Vector Text Embedding" + }, + }, + { + model_id: 'model_3', + inference_config: { + text_classification: {}, // "Text Classification" + }, + }, + ], + }; + const mockModelStats = { + trained_model_stats: mockModelConfigs.trained_model_configs.map((modelConfig) => ({ + model_id: modelConfig.model_id, + })), + }; + + mockTrainedModelsProvider.getTrainedModels.mockImplementation(() => + Promise.resolve(mockModelConfigs) + ); + mockTrainedModelsProvider.getTrainedModelsStats.mockImplementation(() => + Promise.resolve(mockModelStats) + ); + + const models = await fetchMlModels(mockTrainedModelsProvider as unknown as MlTrainedModels); + + expect(models.length).toBe(5); + expect(models[0].modelId).toEqual(ELSER_MODEL_ID); // Pinned to top + expect(models[1].modelId).toEqual(E5_MODEL_ID); // Pinned to top + expect(models[2].modelId).toEqual('model_2'); // "Dense Vector Text Embedding" + expect(models[3].modelId).toEqual('model_1'); // "Named Entity Recognition" + expect(models[4].modelId).toEqual('model_3'); // "Text Classification" + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts new file mode 100644 index 0000000000000..616bcf7277676 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MlTrainedModelConfig, MlTrainedModelStats } from '@elastic/elasticsearch/lib/api/types'; +import { MlTrainedModels } from '@kbn/ml-plugin/server'; + +import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; + +import { + BASE_MODEL, + ELSER_MODEL_ID, + ELSER_MODEL_PLACEHOLDER, + E5_MODEL_ID, + E5_MODEL_PLACEHOLDER, + LANG_IDENT_MODEL_ID, + MODEL_TITLES_BY_TYPE, +} from './utils'; + +/** + * Fetches and enriches trained model information and deployment status. Pins promoted models (ELSER, E5) to the top. If a promoted model doesn't exist, a placeholder will be used. + * + * @param trainedModelsProvider Trained ML models provider + * @returns List of models + */ +export const fetchMlModels = async ( + trainedModelsProvider: MlTrainedModels | undefined +): Promise => { + if (!trainedModelsProvider) { + throw new Error('Machine Learning is not enabled'); + } + + // This array will contain all models, let's add placeholders first + const models: MlModel[] = [ELSER_MODEL_PLACEHOLDER, E5_MODEL_PLACEHOLDER]; + + // Fetch all models and their deployment stats using the ML client + const modelsResponse = await trainedModelsProvider.getTrainedModels({}); + if (modelsResponse.count === 0) { + return models; + } + const modelsStatsResponse = await trainedModelsProvider.getTrainedModelsStats({}); + + modelsResponse.trained_model_configs + // Filter unsupported models + .filter((modelConfig) => isSupportedModel(modelConfig)) + // Get corresponding model stats and compose full model object + .map((modelConfig) => + getModel( + modelConfig, + modelsStatsResponse.trained_model_stats.find((m) => m.model_id === modelConfig.model_id) + ) + ) + // Merge models with placeholders + // (Note: properties from the placeholder that are undefined in the model are preserved) + .forEach((model) => mergeModel(model, models)); + + // Undeployed placeholder models might be in the Downloading phase; let's evaluate this with a call + // We must do this one by one because the API doesn't support fetching multiple models with include=definition_status + for (const model of models) { + if (model.isPromoted && !model.isPlaceholder && !model.hasStats) { + await enrichModelWithDownloadStatus(model, trainedModelsProvider); + } + } + + // Pin ELSER to the top, then E5 below, then the rest of the models sorted alphabetically + return models.sort(sortModels); +}; + +const getModel = (modelConfig: MlTrainedModelConfig, modelStats?: MlTrainedModelStats): MlModel => { + { + const modelId = modelConfig.model_id; + const type = modelConfig.inference_config ? Object.keys(modelConfig.inference_config)[0] : ''; + const model = { + ...BASE_MODEL, + modelId, + type, + title: getUserFriendlyTitle(modelId, type), + isPromoted: [ELSER_MODEL_ID, E5_MODEL_ID].includes(modelId), + }; + + // Enrich deployment stats + if (modelStats && modelStats.deployment_stats) { + model.hasStats = true; + model.deploymentState = getDeploymentState( + modelStats.deployment_stats.allocation_status.state + ); + model.nodeAllocationCount = modelStats.deployment_stats.allocation_status.allocation_count; + model.targetAllocationCount = + modelStats.deployment_stats.allocation_status.target_allocation_count; + model.threadsPerAllocation = modelStats.deployment_stats.threads_per_allocation; + model.startTime = modelStats.deployment_stats.start_time; + } else if (model.modelId === LANG_IDENT_MODEL_ID) { + model.deploymentState = MlModelDeploymentState.FullyAllocated; + } + + return model; + } +}; + +const enrichModelWithDownloadStatus = async ( + model: MlModel, + trainedModelsProvider: MlTrainedModels +) => { + const modelConfigWithDefinitionStatus = await trainedModelsProvider.getTrainedModels({ + model_id: model.modelId, + include: 'definition_status', + }); + + if (modelConfigWithDefinitionStatus && modelConfigWithDefinitionStatus.count > 0) { + model.deploymentState = modelConfigWithDefinitionStatus.trained_model_configs[0].fully_defined + ? MlModelDeploymentState.Downloaded + : MlModelDeploymentState.Downloading; + } +}; + +const mergeModel = (model: MlModel, models: MlModel[]) => { + const i = models.findIndex((m) => m.modelId === model.modelId); + if (i >= 0) { + const { title, ...modelWithoutTitle } = model; + + models[i] = Object.assign({}, models[i], modelWithoutTitle); + } else { + models.push(model); + } +}; + +const isSupportedModel = (modelConfig: MlTrainedModelConfig) => + Object.keys(modelConfig.inference_config || {}).some((inferenceType) => + Object.keys(MODEL_TITLES_BY_TYPE).includes(inferenceType) + ) || modelConfig.model_id === LANG_IDENT_MODEL_ID; + +/** + * Sort function for models; makes ELSER go to the top, then E5, then the rest of the models sorted by title. + */ +const sortModels = (m1: MlModel, m2: MlModel) => + m1.modelId === ELSER_MODEL_ID + ? -1 + : m2.modelId === ELSER_MODEL_ID + ? 1 + : m1.modelId === E5_MODEL_ID + ? -1 + : m2.modelId === E5_MODEL_ID + ? 1 + : m1.title.localeCompare(m2.title); + +const getUserFriendlyTitle = (modelId: string, modelType: string) => { + return MODEL_TITLES_BY_TYPE[modelType] !== undefined + ? MODEL_TITLES_BY_TYPE[modelType]! + : modelId === LANG_IDENT_MODEL_ID + ? 'Lanugage Identification' + : modelId; +}; + +const getDeploymentState = (state: string): MlModelDeploymentState => { + switch (state) { + case 'starting': + return MlModelDeploymentState.Starting; + case 'started': + return MlModelDeploymentState.Started; + case 'fully_allocated': + return MlModelDeploymentState.FullyAllocated; + } + + return MlModelDeploymentState.NotDeployed; +}; diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts b/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts new file mode 100644 index 0000000000000..29aded727280d --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; + +import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; + +export const ELSER_MODEL_ID = '.elser_model_2'; +export const E5_MODEL_ID = '.multilingual-e5-small'; +export const LANG_IDENT_MODEL_ID = 'lang_ident_model_1'; + +export const MODEL_TITLES_BY_TYPE: Record = { + fill_mask: i18n.translate('xpack.enterpriseSearch.content.ml_inference.fill_mask', { + defaultMessage: 'Fill Mask', + }), + lang_ident: i18n.translate('xpack.enterpriseSearch.content.ml_inference.lang_ident', { + defaultMessage: 'Language Identification', + }), + ner: i18n.translate('xpack.enterpriseSearch.content.ml_inference.ner', { + defaultMessage: 'Named Entity Recognition', + }), + question_answering: i18n.translate( + 'xpack.enterpriseSearch.content.ml_inference.question_answering', + { + defaultMessage: 'Question Answering', + } + ), + text_classification: i18n.translate( + 'xpack.enterpriseSearch.content.ml_inference.text_classification', + { + defaultMessage: 'Text Classification', + } + ), + text_embedding: i18n.translate('xpack.enterpriseSearch.content.ml_inference.text_embedding', { + defaultMessage: 'Dense Vector Text Embedding', + }), + text_expansion: i18n.translate('xpack.enterpriseSearch.content.ml_inference.text_expansion', { + defaultMessage: 'Elastic Learned Sparse EncodeR (ELSER)', + }), + zero_shot_classification: i18n.translate( + 'xpack.enterpriseSearch.content.ml_inference.zero_shot_classification', + { + defaultMessage: 'Zero-Shot Text Classification', + } + ), +}; + +export const BASE_MODEL = { + deploymentState: MlModelDeploymentState.NotDeployed, + nodeAllocationCount: 0, + startTime: 0, + targetAllocationCount: 0, + threadsPerAllocation: 0, + isPlaceholder: false, + hasStats: false, +}; + +export const ELSER_MODEL_PLACEHOLDER: MlModel = { + ...BASE_MODEL, + modelId: ELSER_MODEL_ID, + type: SUPPORTED_PYTORCH_TASKS.TEXT_EXPANSION, + title: 'Elastic Learned Sparse EncodeR (ELSER)', + description: i18n.translate('xpack.enterpriseSearch.modelCard.elserPlaceholder.description', { + defaultMessage: + 'ELSER is designed to efficiently use context in natural language queries with better results than BM25 alone.', + }), + license: 'Elastic', + isPlaceholder: true, +}; + +export const E5_MODEL_PLACEHOLDER: MlModel = { + ...BASE_MODEL, + modelId: E5_MODEL_ID, + type: SUPPORTED_PYTORCH_TASKS.TEXT_EMBEDDING, + title: 'E5 Multilingual Embedding', + description: i18n.translate('xpack.enterpriseSearch.modelCard.e5Placeholder.description', { + defaultMessage: 'Multilingual dense vector embedding generator.', + }), + license: 'MIT', + modelDetailsPageUrl: 'https://huggingface.co/intfloat/multilingual-e5-small', + isPlaceholder: true, +}; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts index e0278ff639232..eb2462457d438 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts @@ -60,6 +60,9 @@ jest.mock('../../lib/indices/pipelines/ml_inference/get_ml_inference_errors', () jest.mock('../../lib/pipelines/ml_inference/get_ml_inference_pipelines', () => ({ getMlInferencePipelines: jest.fn(), })); +jest.mock('../../lib/ml/fetch_ml_models', () => ({ + fetchMlModels: jest.fn(), +})); jest.mock('../../lib/ml/get_ml_model_deployment_status', () => ({ getMlModelDeploymentStatus: jest.fn(), })); @@ -85,6 +88,7 @@ import { preparePipelineAndIndexForMlInference } from '../../lib/indices/pipelin import { deleteMlInferencePipeline } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/delete_ml_inference_pipeline'; import { detachMlInferencePipeline } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/detach_ml_inference_pipeline'; import { fetchMlInferencePipelineProcessors } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/get_ml_inference_pipeline_processors'; +import { fetchMlModels } from '../../lib/ml/fetch_ml_models'; import { getMlModelDeploymentStatus } from '../../lib/ml/get_ml_model_deployment_status'; import { startMlModelDeployment } from '../../lib/ml/start_ml_model_deployment'; import { startMlModelDownload } from '../../lib/ml/start_ml_model_download'; @@ -1175,6 +1179,58 @@ describe('Enterprise Search Managed Indices', () => { }); }); + describe('GET /internal/enterprise_search/ml/models', () => { + let mockMl: MlPluginSetup; + let mockTrainedModelsProvider: MlTrainedModels; + + beforeEach(() => { + const context = { + core: Promise.resolve(mockCore), + } as unknown as jest.Mocked; + + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/ml/models', + }); + + mockTrainedModelsProvider = { + getTrainedModels: jest.fn(), + getTrainedModelsStats: jest.fn(), + } as unknown as MlTrainedModels; + + mockMl = { + trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), + } as unknown as jest.Mocked; + + registerIndexRoutes({ + ...mockDependencies, + ml: mockMl, + router: mockRouter.router, + }); + }); + + it('fetches models', async () => { + const request = {}; + + const mockResponse = [ + { + modelId: 'model_1', + deploymentState: MlModelDeploymentState.Starting, + }, + ]; + + (fetchMlModels as jest.Mock).mockResolvedValueOnce(mockResponse); + + await mockRouter.callRoute(request); + + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: mockResponse, + headers: { 'content-type': 'application/json' }, + }); + }); + }); + describe('GET /internal/enterprise_search/ml/models/{modelName}', () => { let mockMl: MlPluginSetup; let mockTrainedModelsProvider: MlTrainedModels; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts index a43846aafab6a..d96130fb02472 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts @@ -41,6 +41,7 @@ import { preparePipelineAndIndexForMlInference } from '../../lib/indices/pipelin import { deleteMlInferencePipeline } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/delete_ml_inference_pipeline'; import { detachMlInferencePipeline } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/detach_ml_inference_pipeline'; import { fetchMlInferencePipelineProcessors } from '../../lib/indices/pipelines/ml_inference/pipeline_processors/get_ml_inference_pipeline_processors'; +import { fetchMlModels } from '../../lib/ml/fetch_ml_models'; import { getMlModelDeploymentStatus } from '../../lib/ml/get_ml_model_deployment_status'; import { startMlModelDeployment } from '../../lib/ml/start_ml_model_deployment'; import { startMlModelDownload } from '../../lib/ml/start_ml_model_download'; @@ -1100,6 +1101,28 @@ export function registerIndexRoutes({ }) ); + router.get( + { + path: '/internal/enterprise_search/ml/models', + validate: {}, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const { + savedObjects: { client: savedObjectsClient }, + } = await context.core; + const trainedModelsProvider = ml + ? await ml.trainedModelsProvider(request, savedObjectsClient) + : undefined; + + const modelsResult = await fetchMlModels(trainedModelsProvider); + + return response.ok({ + body: modelsResult, + headers: { 'content-type': 'application/json' }, + }); + }) + ); + router.get( { path: '/internal/enterprise_search/ml/models/{modelName}', diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx index 4e81ca840199f..5d43f5bd1e6df 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx @@ -12,8 +12,6 @@ import { sampleAttribute } from '../../configurations/test_data/sample_attribute import * as pluginHook from '../../../../../hooks/use_plugin_context'; import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; import { ExpViewActionMenuContent } from './action_menu'; -import { noCasesPermissions as mockUseGetCasesPermissions } from '@kbn/observability-shared-plugin/public'; -import * as obsHooks from '@kbn/observability-shared-plugin/public/hooks/use_get_user_cases_permissions'; jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ appMountParameters: { @@ -21,13 +19,6 @@ jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ }, } as any); -jest.spyOn(obsHooks, 'useGetUserCasesPermissions').mockImplementation( - () => - ({ - useGetUserCasesPermissions: jest.fn(() => mockUseGetCasesPermissions()), - } as any) -); - describe('Action Menu', function () { afterAll(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx index 7b4e0cb5cc57f..83cae3e8b4ebb 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -12,8 +12,6 @@ import { ExploratoryView } from './exploratory_view'; import * as obsvDataViews from '../../../utils/observability_data_views/observability_data_views'; import * as pluginHook from '../../../hooks/use_plugin_context'; import { createStubIndexPattern } from '@kbn/data-plugin/common/stubs'; -import { noCasesPermissions as mockUseGetCasesPermissions } from '@kbn/observability-shared-plugin/public/utils/cases_permissions'; -import * as obsHooks from '@kbn/observability-shared-plugin/public/hooks/use_get_user_cases_permissions'; jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ appMountParameters: { @@ -21,13 +19,6 @@ jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ }, } as any); -jest.spyOn(obsHooks, 'useGetUserCasesPermissions').mockImplementation( - () => - ({ - useGetUserCasesPermissions: jest.fn(() => mockUseGetCasesPermissions()), - } as any) -); - describe('ExploratoryView', () => { mockAppDataView(); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx index ffccfdf6db3f2..1d42716bf405d 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx @@ -13,10 +13,15 @@ import * as useCaseHook from '../hooks/use_add_to_case'; import * as datePicker from '../components/date_range_picker'; import moment from 'moment'; import { noCasesPermissions as mockUseGetCasesPermissions } from '@kbn/observability-shared-plugin/public'; -import * as obsHooks from '@kbn/observability-shared-plugin/public/hooks/use_get_user_cases_permissions'; -jest.spyOn(obsHooks, 'useGetUserCasesPermissions').mockReturnValue(mockUseGetCasesPermissions()); describe('AddToCaseAction', function () { + const coreRenderProps = { + cases: { + ui: { getAllCasesSelectorModal: jest.fn() }, + helpers: { canUseCases: () => mockUseGetCasesPermissions() }, + }, + }; + beforeEach(() => { jest.spyOn(datePicker, 'parseRelativeDate').mockRestore(); }); @@ -26,7 +31,8 @@ describe('AddToCaseAction', function () { + />, + { core: coreRenderProps } ); expect(await findByText('Add to case')).toBeInTheDocument(); }); @@ -39,7 +45,8 @@ describe('AddToCaseAction', function () { + />, + { core: coreRenderProps } ); expect(await findByText('Add to case')).toBeInTheDocument(); @@ -60,7 +67,8 @@ describe('AddToCaseAction', function () { const useAddToCaseHook = jest.spyOn(useCaseHook, 'useAddToCase'); const { getByText } = render( - + , + { core: coreRenderProps } ); expect(await forNearestButton(getByText)('Add to case')).toBeDisabled(); @@ -95,7 +103,7 @@ describe('AddToCaseAction', function () { lensAttributes={{ title: 'Performance distribution' } as any} timeRange={{ to: 'now', from: 'now-5m' }} />, - { initSeries } + { initSeries, core: coreRenderProps } ); fireEvent.click(await findByText('Add to case')); @@ -111,6 +119,7 @@ describe('AddToCaseAction', function () { delete: false, push: false, connectors: false, + settings: false, }, }) ); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx index 1cbb904f6500d..590451eaea6ef 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/header/add_to_case_action.tsx @@ -16,7 +16,6 @@ import { } from '@kbn/cases-plugin/public'; import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; import { observabilityFeatureId } from '@kbn/observability-shared-plugin/public'; -import { useGetUserCasesPermissions } from '@kbn/observability-shared-plugin/public'; import { ObservabilityAppServices } from '../../../../application/types'; import { useAddToCase } from '../hooks/use_add_to_case'; import { parseRelativeDate } from '../components/date_range_picker'; @@ -37,7 +36,7 @@ export function AddToCaseAction({ timeRange, }: AddToCaseProps) { const kServices = useKibana().services; - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = kServices.cases.helpers.canUseCases([observabilityFeatureId]); const { cases, diff --git a/x-pack/plugins/features/common/feature_kibana_privileges.ts b/x-pack/plugins/features/common/feature_kibana_privileges.ts index 3a56b080bb91f..49c001c890b69 100644 --- a/x-pack/plugins/features/common/feature_kibana_privileges.ts +++ b/x-pack/plugins/features/common/feature_kibana_privileges.ts @@ -204,6 +204,16 @@ export interface FeatureKibanaPrivileges { * ``` */ delete?: readonly string[]; + /** + * List of case owners which users should have settings access to when granted this privilege. + * @example + * ```ts + * { + * settings: ['securitySolution'] + * } + * ``` + */ + settings?: readonly string[]; }; /** diff --git a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap index 62e868e77e520..7247c1d23cb0c 100644 --- a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap +++ b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap @@ -558,6 +558,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ @@ -710,6 +711,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ @@ -1032,6 +1034,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ @@ -1169,6 +1172,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ @@ -1321,6 +1325,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ @@ -1643,6 +1648,7 @@ Array [ "delete": Array [], "push": Array [], "read": Array [], + "settings": Array [], "update": Array [], }, "catalogue": Array [ diff --git a/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.test.ts b/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.test.ts index f2f6ed1071f81..58a39c85bf9e9 100644 --- a/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.test.ts +++ b/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.test.ts @@ -77,6 +77,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -146,6 +147,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -214,6 +216,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -284,6 +287,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -324,6 +328,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -385,6 +390,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -431,6 +437,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -498,6 +505,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -559,6 +567,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -605,6 +614,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -672,6 +682,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -734,6 +745,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -783,6 +795,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type', 'cases-update-sub-type'], delete: ['cases-delete-type', 'cases-delete-sub-type'], push: ['cases-push-type', 'cases-push-sub-type'], + settings: ['cases-settings-type', 'cases-settings-sub-type'], }, ui: ['ui-action', 'ui-sub-type'], }, @@ -818,6 +831,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-action', 'ui-sub-type'], }, @@ -860,6 +874,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -964,6 +979,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -998,6 +1014,7 @@ describe('featurePrivilegeIterator', () => { update: [], delete: [], push: [], + settings: [], }, ui: ['ui-action'], }, @@ -1038,6 +1055,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -1100,6 +1118,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -1149,6 +1168,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type', 'cases-update-sub-type'], delete: ['cases-delete-type', 'cases-delete-sub-type'], push: ['cases-push-type', 'cases-push-sub-type'], + settings: ['cases-settings-type', 'cases-settings-sub-type'], }, ui: ['ui-action', 'ui-sub-type'], }, @@ -1341,6 +1361,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -1390,6 +1411,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -1425,6 +1447,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-sub-type'], delete: ['cases-delete-sub-type'], push: ['cases-push-sub-type'], + settings: ['cases-settings-sub-type'], }, ui: ['ui-sub-type'], }, @@ -1465,6 +1488,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -1555,6 +1579,7 @@ describe('featurePrivilegeIterator', () => { update: ['cases-update-type'], delete: ['cases-delete-type'], push: ['cases-push-type'], + settings: ['cases-settings-type'], }, ui: ['ui-action'], }, @@ -1589,6 +1614,7 @@ describe('featurePrivilegeIterator', () => { update: [], delete: [], push: [], + settings: [], }, ui: ['ui-action'], }, diff --git a/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.ts b/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.ts index 9392b3b3fee33..0d1dc8e3ab788 100644 --- a/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.ts +++ b/x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.ts @@ -147,6 +147,10 @@ function mergeWithSubFeatures( subFeaturePrivilege.cases?.delete ?? [] ), push: mergeArrays(mergedConfig.cases?.push ?? [], subFeaturePrivilege.cases?.push ?? []), + settings: mergeArrays( + mergedConfig.cases?.settings ?? [], + subFeaturePrivilege.cases?.settings ?? [] + ), }; } return mergedConfig; diff --git a/x-pack/plugins/features/server/feature_schema.ts b/x-pack/plugins/features/server/feature_schema.ts index 416a9bf534b3a..b332ea355dcc0 100644 --- a/x-pack/plugins/features/server/feature_schema.ts +++ b/x-pack/plugins/features/server/feature_schema.ts @@ -82,6 +82,7 @@ const casesSchemaObject = schema.maybe( update: schema.maybe(casesSchema), delete: schema.maybe(casesSchema), push: schema.maybe(casesSchema), + settings: schema.maybe(casesSchema), }) ); diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index e0b754430521d..102a80ad003fd 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -6069,6 +6069,35 @@ "install_format_schema_version": { "type": "string" }, + "latest_install_failed_attempts": { + "description": "Latest failed install errors", + "type": "array", + "items": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "target_version": { + "type": "string" + }, + "error": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "message": { + "type": "string" + }, + "stack": { + "type": "string" + } + } + } + } + } + }, "verification_status": { "type": "string", "enum": [ @@ -6120,7 +6149,8 @@ "install_version", "install_started_at", "install_source", - "verification_status" + "verification_status", + "latest_install_failed_attempts" ] }, "search_result": { @@ -8257,6 +8287,50 @@ "type" ] }, + "output_create_request_remote_elasticsearch": { + "title": "remote_elasticsearch", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "is_default": { + "type": "boolean" + }, + "is_default_monitoring": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "remote_elasticsearch" + ] + }, + "hosts": { + "type": "array", + "items": { + "type": "string" + } + }, + "service_token": { + "type": "string" + }, + "secrets": { + "type": "object", + "properties": { + "service_token": { + "type": "string" + } + } + } + }, + "required": [ + "name" + ] + }, "output_create_request": { "title": "Output", "oneOf": [ @@ -8268,6 +8342,9 @@ }, { "$ref": "#/components/schemas/output_create_request_logstash" + }, + { + "$ref": "#/components/schemas/output_create_request_remote_elasticsearch" } ], "discriminator": { @@ -8275,7 +8352,8 @@ "mapping": { "elasticsearch": "#/components/schemas/output_create_request_elasticsearch", "kafka": "#/components/schemas/output_create_request_kafka", - "logstash": "#/components/schemas/output_create_request_logstash" + "logstash": "#/components/schemas/output_create_request_logstash", + "remote_elasticsearch": "#/components/schemas/output_create_request_remote_elasticsearch" } } }, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index d977af4f9c2b5..2e47aaf003062 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -3824,6 +3824,25 @@ components: type: string install_format_schema_version: type: string + latest_install_failed_attempts: + description: Latest failed install errors + type: array + items: + type: object + properties: + created_at: + type: string + target_version: + type: string + error: + type: object + properties: + name: + type: string + message: + type: string + stack: + type: string verification_status: type: string enum: @@ -3863,6 +3882,7 @@ components: - install_started_at - install_source - verification_status + - latest_install_failed_attempts search_result: title: Search result type: object @@ -5331,18 +5351,49 @@ components: - name - hosts - type + output_create_request_remote_elasticsearch: + title: remote_elasticsearch + type: object + properties: + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + name: + type: string + type: + type: string + enum: + - remote_elasticsearch + hosts: + type: array + items: + type: string + service_token: + type: string + secrets: + type: object + properties: + service_token: + type: string + required: + - name output_create_request: title: Output oneOf: - $ref: '#/components/schemas/output_create_request_elasticsearch' - $ref: '#/components/schemas/output_create_request_kafka' - $ref: '#/components/schemas/output_create_request_logstash' + - $ref: '#/components/schemas/output_create_request_remote_elasticsearch' discriminator: propertyName: type mapping: elasticsearch: '#/components/schemas/output_create_request_elasticsearch' kafka: '#/components/schemas/output_create_request_kafka' logstash: '#/components/schemas/output_create_request_logstash' + remote_elasticsearch: '#/components/schemas/output_create_request_remote_elasticsearch' output_update_request_elasticsearch: title: elasticsearch type: object diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/installation_info.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/installation_info.yaml index 4d13611a6afbd..c5db5f12d4cc3 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/installation_info.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/installation_info.yaml @@ -47,6 +47,25 @@ properties: type: string install_format_schema_version: type: string + latest_install_failed_attempts: + description: Latest failed install errors + type: array + items: + type: object + properties: + created_at: + type: string + target_version: + type: string + error: + type: object + properties: + name: + type: string + message: + type: string + stack: + type: string verification_status: type: string enum: @@ -86,3 +105,4 @@ required: - install_started_at - install_source - verification_status + - latest_install_failed_attempts diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request.yaml index 21506825cfd61..9fc0ad6d24590 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request.yaml @@ -3,9 +3,11 @@ oneOf: - $ref: './output_create_request_elasticsearch.yaml' - $ref: './output_create_request_kafka.yaml' - $ref: './output_create_request_logstash.yaml' + - $ref: './output_create_request_remote_elasticsearch.yaml' discriminator: propertyName: type mapping: elasticsearch: './output_create_request_elasticsearch.yaml' kafka: './output_create_request_kafka.yaml' logstash: './output_create_request_logstash.yaml' + remote_elasticsearch: './output_create_request_remote_elasticsearch.yaml' diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_remote_elasticsearch.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_remote_elasticsearch.yaml new file mode 100644 index 0000000000000..844b92df39b84 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_remote_elasticsearch.yaml @@ -0,0 +1,27 @@ +title: remote_elasticsearch +type: object +properties: + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + name: + type: string + type: + type: string + enum: ['remote_elasticsearch'] + hosts: + type: array + items: + type: string + service_token: + type: string + secrets: + type: object + properties: + service_token: + type: string +required: + - name diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index c4d4e9c763766..24bde5b5f5f5a 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -530,6 +530,16 @@ export interface ExperimentalDataStreamFeature { features: Partial>; } +export interface InstallFailedAttempt { + created_at: string; + target_version: string; + error: { + name: string; + message: string; + stack?: string; + }; +} + export interface Installation { installed_kibana: KibanaAssetReference[]; installed_es: EsAssetReference[]; @@ -549,6 +559,7 @@ export interface Installation { experimental_data_stream_features?: ExperimentalDataStreamFeature[]; internal?: boolean; removable?: boolean; + latest_install_failed_attempts?: InstallFailedAttempt[]; } export interface PackageUsageStats { diff --git a/x-pack/plugins/fleet/common/types/models/output.ts b/x-pack/plugins/fleet/common/types/models/output.ts index 3283f4d01e540..8d721509495ea 100644 --- a/x-pack/plugins/fleet/common/types/models/output.ts +++ b/x-pack/plugins/fleet/common/types/models/output.ts @@ -23,7 +23,12 @@ export type KafkaPartitionType = typeof kafkaPartitionType; export type KafkaTopicWhenType = typeof kafkaTopicWhenType; export type KafkaAcknowledgeReliabilityLevel = typeof kafkaAcknowledgeReliabilityLevel; export type KafkaVerificationMode = typeof kafkaVerificationModes; - +export type OutputSecret = + | string + | { + id: string; + hash?: string; + }; interface NewBaseOutput { is_default: boolean; is_default_monitoring: boolean; @@ -43,15 +48,7 @@ interface NewBaseOutput { proxy_id?: string | null; shipper?: ShipperOutput | null; allow_edit?: string[]; - secrets?: { - ssl?: { - key?: - | string - | { - id: string; - }; - }; - }; + secrets?: {}; } export interface NewElasticsearchOutput extends NewBaseOutput { @@ -61,10 +58,18 @@ export interface NewElasticsearchOutput extends NewBaseOutput { export interface NewRemoteElasticsearchOutput extends NewBaseOutput { type: OutputType['RemoteElasticsearch']; service_token?: string; + secrets?: { + service_token?: OutputSecret; + }; } export interface NewLogstashOutput extends NewBaseOutput { type: OutputType['Logstash']; + secrets?: { + ssl?: { + key?: OutputSecret; + }; + }; } export type NewOutput = @@ -131,17 +136,9 @@ export interface KafkaOutput extends NewBaseOutput { broker_timeout?: number; required_acks?: ValueOf; secrets?: { - password?: - | string - | { - id: string; - }; + password?: OutputSecret; ssl?: { - key?: - | string - | { - id: string; - }; + key?: OutputSecret; }; }; } diff --git a/x-pack/plugins/fleet/cypress/tasks/common.ts b/x-pack/plugins/fleet/cypress/tasks/common.ts index ebb631b310b17..6d922f6b003fa 100644 --- a/x-pack/plugins/fleet/cypress/tasks/common.ts +++ b/x-pack/plugins/fleet/cypress/tasks/common.ts @@ -49,6 +49,7 @@ export const request = ({ const NEW_FEATURES_TOUR_STORAGE_KEYS = { RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.9', + TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour', }; const disableNewFeaturesTours = (window: Window) => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index c2517c245839d..9321f77886085 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -338,8 +338,8 @@ const SecretFieldWrapper = ({ children }: { children: React.ReactNode }) => { const SecretFieldLabel = ({ fieldLabel }: { fieldLabel: string }) => { return ( <> - - + + {fieldLabel} @@ -376,12 +376,37 @@ function SecretInputField({ setIsDirty, isDirty, }: InputComponentProps) { - const [editMode, setEditMode] = useState(isEditPage && !value); + const [isReplacing, setIsReplacing] = useState(isEditPage && !value); const valueOnFirstRender = useRef(value); + const hasExistingValue = !!valueOnFirstRender.current; const lowercaseTitle = varDef.title?.toLowerCase(); + const showInactiveReplaceUi = isEditPage && !isReplacing && hasExistingValue; + const valueIsSecretRef = value && value?.isSecretRef; + + const inputComponent = getInputComponent({ + varDef, + value: isReplacing && valueIsSecretRef ? '' : value, + onChange, + frozen, + packageName, + packageType, + datastreams, + isEditPage, + isInvalid, + fieldLabel, + fieldTestSelector, + isDirty, + setIsDirty, + }); + + // If there's no value for this secret, display the input as its "brand new" creation state + // instead of the "replace" state + if (!hasExistingValue) { + return inputComponent; + } - if (isEditPage && !editMode) { + if (showInactiveReplaceUi) { return ( <> @@ -395,7 +420,7 @@ function SecretInputField({ setEditMode(true)} + onClick={() => setIsReplacing(true)} color="primary" iconType="refresh" iconSide="left" @@ -413,28 +438,11 @@ function SecretInputField({ ); } - const valueIsSecretRef = value && value?.isSecretRef; - const field = getInputComponent({ - varDef, - value: editMode && valueIsSecretRef ? '' : value, - onChange, - frozen, - packageName, - packageType, - datastreams, - isEditPage, - isInvalid, - fieldLabel, - fieldTestSelector, - isDirty, - setIsDirty, - }); - - if (editMode) { + if (isReplacing) { const cancelButton = ( { - setEditMode(false); + setIsReplacing(false); setIsDirty(false); onChange(valueOnFirstRender.current); }} @@ -455,12 +463,12 @@ function SecretInputField({ return ( - {field} + {inputComponent} {cancelButton} ); } - return field; + return inputComponent; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index 2308cc824db6d..2126a275818e1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -191,7 +191,9 @@ describe('EditOutputFlyout', () => { }); it('should render the flyout if the output provided is a remote ES output', async () => { - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ remoteESOutput: true }); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ remoteESOutput: true, outputSecretsStorage: true }); const { utils } = renderFlyout({ type: 'remote_elasticsearch', name: 'remote es output', @@ -208,6 +210,8 @@ describe('EditOutputFlyout', () => { expect(utils.queryByTestId('settingsOutputsFlyout.typeInput')?.textContent).toContain( 'Remote Elasticsearch' ); + + expect(utils.queryByTestId('serviceTokenSecretInput')).not.toBeNull(); }); it('should not display remote ES output in type lists if serverless', async () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 0346986f3abbe..0e742876af82d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -272,7 +272,13 @@ export const EditOutputFlyout: React.FunctionComponent = const renderRemoteElasticsearchSection = () => { if (isRemoteESOutputEnabled) { - return ; + return ( + + ); } return null; }; @@ -322,7 +328,7 @@ export const EditOutputFlyout: React.FunctionComponent = case outputType.Elasticsearch: return i18n.translate('xpack.fleet.settings.editOutputFlyout.esOutputTypeCallout', { defaultMessage: - 'This output type does not support connectivity to a remote Elasticsearch cluster, please the Remote Elasticsearch type for that.', + 'This output type does not support connectivity to a remote Elasticsearch cluster, please use the Remote Elasticsearch type for that.', }); } }; @@ -335,7 +341,7 @@ export const EditOutputFlyout: React.FunctionComponent = defaultMessage="Enter your output hosts, service token for your remote cluster, and any advanced YAML configuration. Learn more about how to use these parameters in {doc}." values={{ doc: ( - + {i18n.translate('xpack.fleet.settings.editOutputFlyout.docLabel', { defaultMessage: 'our documentation', })} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx index 9b4c8f085af9a..9e5fe1bc519fb 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx @@ -13,13 +13,16 @@ import { i18n } from '@kbn/i18n'; import { MultiRowInput } from '../multi_row_input'; import type { OutputFormInputsType } from './use_output_form'; +import { SecretFormRow } from './output_form_secret_form_row'; interface Props { inputs: OutputFormInputsType; + useSecretsStorage: boolean; + onUsePlainText: () => void; } export const OutputFormRemoteEsSection: React.FunctionComponent = (props) => { - const { inputs } = props; + const { inputs, useSecretsStorage, onUsePlainText } = props; return ( <> @@ -38,27 +41,50 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) isUrl /> - + } + {...inputs.serviceTokenInput.formRowProps} + > + - } - {...inputs.serviceTokenInput.formRowProps} - > - + ) : ( + - + title={i18n.translate('xpack.fleet.settings.editOutputFlyout.serviceTokenLabel', { + defaultMessage: 'Service Token', + })} + {...inputs.serviceTokenSecretInput.formRowProps} + onUsePlainText={onUsePlainText} + > + + + )} ; caTrustedFingerprintInput: ReturnType; serviceTokenInput: ReturnType; + serviceTokenSecretInput: ReturnType; sslCertificateInput: ReturnType; sslKeyInput: ReturnType; sslKeySecretInput: ReturnType; @@ -215,6 +217,12 @@ export function useOutputForm(onSucess: () => void, output?: Output) { validateServiceToken, isDisabled('service_token') ); + + const serviceTokenSecretInput = useSecretInput( + (output as NewRemoteElasticsearchOutput)?.secrets?.service_token ?? '', + validateServiceTokenSecret, + isDisabled('service_token') + ); /* Shipper feature flag - currently depends on the content of the yaml # Enables the shipper: @@ -293,7 +301,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { const sslKeyInput = useInput(output?.ssl?.key ?? '', validateSSLKey, isSSLEditable); const sslKeySecretInput = useSecretInput( - output?.secrets?.ssl?.key, + (output as NewLogstashOutput)?.secrets?.ssl?.key, validateSSLKeySecret, isSSLEditable ); @@ -503,6 +511,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { defaultMonitoringOutputInput, caTrustedFingerprintInput, serviceTokenInput, + serviceTokenSecretInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -562,6 +571,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { const additionalYamlConfigValid = additionalYamlConfigInput.validate(); const caTrustedFingerprintValid = caTrustedFingerprintInput.validate(); const serviceTokenValid = serviceTokenInput.validate(); + const serviceTokenSecretValid = serviceTokenSecretInput.validate(); const sslCertificateValid = sslCertificateInput.validate(); const sslKeyValid = sslKeyInput.validate(); const sslKeySecretValid = sslKeySecretInput.validate(); @@ -607,7 +617,11 @@ export function useOutputForm(onSucess: () => void, output?: Output) { } if (isRemoteElasticsearch) { return ( - elasticsearchUrlsValid && additionalYamlConfigValid && nameInputValid && serviceTokenValid + elasticsearchUrlsValid && + additionalYamlConfigValid && + nameInputValid && + ((serviceTokenInput.value && serviceTokenValid) || + (serviceTokenSecretInput.value && serviceTokenSecretValid)) ); } else { // validate ES @@ -637,6 +651,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { additionalYamlConfigInput, caTrustedFingerprintInput, serviceTokenInput, + serviceTokenSecretInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -851,7 +866,13 @@ export function useOutputForm(onSucess: () => void, output?: Output) { is_default: false, is_default_monitoring: defaultMonitoringOutputInput.value, config_yaml: additionalYamlConfigInput.value, - service_token: serviceTokenInput.value, + service_token: serviceTokenInput.value || undefined, + ...(!serviceTokenInput.value && + serviceTokenSecretInput.value && { + secrets: { + service_token: serviceTokenSecretInput.value, + }, + }), proxy_id: proxyIdValue, ...shipperParams, } as NewRemoteElasticsearchOutput; @@ -958,6 +979,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { elasticsearchUrlInput.value, caTrustedFingerprintInput.value, serviceTokenInput.value, + serviceTokenSecretInput.value, confirm, notifications.toasts, ]); diff --git a/x-pack/plugins/fleet/public/applications/integrations/components/header/deployment_details.tsx b/x-pack/plugins/fleet/public/applications/integrations/components/header/deployment_details.tsx index ea17bc3f201c4..dd43244da0c41 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/components/header/deployment_details.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/components/header/deployment_details.tsx @@ -32,9 +32,9 @@ export const DeploymentDetails = () => { } const button = ( - setIsOpen(!isOpen)} iconType="iInCircle" iconSide="left" isActive> - {i18n.translate('xpack.fleet.integrations.endpointsButton', { - defaultMessage: 'Endpoints', + setIsOpen(!isOpen)} isActive> + {i18n.translate('xpack.fleet.integrations.connectionDetailsButton', { + defaultMessage: 'Connection details', })} ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx index 2d08fb50af8be..2177f8e21436c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx @@ -48,6 +48,7 @@ export function PackageCard({ isUnverified, isUpdateAvailable, showLabels = true, + extraLabelsBadges, }: PackageCardProps) { let releaseBadge: React.ReactNode | null = null; @@ -63,7 +64,6 @@ export function PackageCard({ } let verifiedBadge: React.ReactNode | null = null; - if (isUnverified && showLabels) { verifiedBadge = ( @@ -106,7 +106,7 @@ export function PackageCard({ - + + {showLabels && extraLabelsBadges ? extraLabelsBadges : null} {verifiedBadge} {updateAvailableBadge} {releaseBadge} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx new file mode 100644 index 0000000000000..327d550df5780 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { createIntegrationsTestRendererMock } from '../../../../../../mock'; +import type { PackageListItem } from '../../../../types'; + +import { getIntegrationLabels } from './card_utils'; + +function renderIntegrationLabels(item: Partial) { + const renderer = createIntegrationsTestRendererMock(); + + return renderer.render(<>{getIntegrationLabels(item as any)}); +} + +describe('Card utils', () => { + describe('getIntegrationLabels', () => { + it('should return an empty list for an integration without errors', () => { + const res = renderIntegrationLabels({ + installationInfo: { + install_status: 'installed', + } as any, + }); + const badges = res.container.querySelectorAll('.euiBadge'); + expect(badges).toHaveLength(0); + }); + + it('should return a badge for install_failed for an integration with status:install_failled', () => { + const res = renderIntegrationLabels({ + installationInfo: { + install_status: 'install_failed', + } as any, + }); + const badges = res.container.querySelectorAll('.euiBadge'); + expect(badges).toHaveLength(1); + expect(res.queryByText('Install failed')).not.toBeNull(); + }); + + it('should return a badge if there is an upgrade failed in the last_attempt_errors', () => { + const res = renderIntegrationLabels({ + installationInfo: { + version: '1.0.0', + install_status: 'installed', + latest_install_failed_attempts: [ + { + created_at: new Date().toISOString(), + error: { + name: 'Test', + message: 'test error 123', + }, + target_version: '2.0.0', + }, + ], + } as any, + }); + const badges = res.container.querySelectorAll('.euiBadge'); + expect(badges).toHaveLength(1); + expect(res.queryByText('Update failed')).not.toBeNull(); + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx new file mode 100644 index 0000000000000..910b872bf5ceb --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedMessage, FormattedDate, FormattedTime } from '@kbn/i18n-react'; +import { EuiBadge, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; +import semverLt from 'semver/functions/lt'; + +import type { + CustomIntegration, + CustomIntegrationIcon, +} from '@kbn/custom-integrations-plugin/common'; + +import { hasDeferredInstallations } from '../../../../../../services/has_deferred_installations'; +import { getPackageReleaseLabel } from '../../../../../../../common/services'; + +import { installationStatuses } from '../../../../../../../common/constants'; +import type { + InstallFailedAttempt, + IntegrationCardReleaseLabel, + PackageSpecIcon, +} from '../../../../../../../common/types'; + +import type { DynamicPage, DynamicPagePathValues, StaticPage } from '../../../../constants'; +import { isPackageUnverified, isPackageUpdatable } from '../../../../services'; + +import type { PackageListItem } from '../../../../types'; + +export interface IntegrationCardItem { + url: string; + release?: IntegrationCardReleaseLabel; + description: string; + name: string; + title: string; + version: string; + icons: Array; + integration: string; + id: string; + categories: string[]; + fromIntegrations?: string; + isReauthorizationRequired?: boolean; + isUnverified?: boolean; + isUpdateAvailable?: boolean; + showLabels?: boolean; + extraLabelsBadges?: React.ReactNode[]; +} + +export const mapToCard = ({ + getAbsolutePath, + getHref, + item, + addBasePath, + packageVerificationKeyId, + selectedCategory, +}: { + getAbsolutePath: (p: string) => string; + getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => string; + addBasePath: (url: string) => string; + item: CustomIntegration | PackageListItem; + packageVerificationKeyId?: string; + selectedCategory?: string; +}): IntegrationCardItem => { + let uiInternalPathUrl: string; + + let isUnverified = false; + + const version = 'version' in item ? item.version || '' : ''; + + let isUpdateAvailable = false; + let isReauthorizationRequired = false; + if (item.type === 'ui_link') { + uiInternalPathUrl = item.id.includes('language_client.') + ? addBasePath(item.uiInternalPath) + : item.uiExternalLink || getAbsolutePath(item.uiInternalPath); + } else { + let urlVersion = item.version; + if (item?.installationInfo?.version) { + urlVersion = item.installationInfo.version || item.version; + isUnverified = isPackageUnverified(item, packageVerificationKeyId); + isUpdateAvailable = isPackageUpdatable(item); + + isReauthorizationRequired = hasDeferredInstallations(item); + } + + const url = getHref('integration_details_overview', { + pkgkey: `${item.name}-${urlVersion}`, + ...(item.integration ? { integration: item.integration } : {}), + }); + + uiInternalPathUrl = url; + } + + const release: IntegrationCardReleaseLabel = getPackageReleaseLabel(version); + + let extraLabelsBadges: React.ReactNode[] | undefined; + if (item.type === 'integration') { + extraLabelsBadges = getIntegrationLabels(item); + } + + return { + id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}:${item.id}`, + description: item.description, + icons: !item.icons || !item.icons.length ? [] : item.icons, + title: item.title, + url: uiInternalPathUrl, + fromIntegrations: selectedCategory, + integration: 'integration' in item ? item.integration || '' : '', + name: 'name' in item ? item.name : item.id, + version, + release, + categories: ((item.categories || []) as string[]).filter((c: string) => !!c), + isReauthorizationRequired, + isUnverified, + isUpdateAvailable, + extraLabelsBadges, + }; +}; + +export function getIntegrationLabels(item: PackageListItem): React.ReactNode[] { + const extraLabelsBadges: React.ReactNode[] = []; + + if ( + item?.installationInfo?.latest_install_failed_attempts?.some( + (attempt) => + item.installationInfo && semverLt(item.installationInfo.version, attempt.target_version) + ) + ) { + const updateFailedAttempt = item.installationInfo?.latest_install_failed_attempts?.find( + (attempt) => + item.installationInfo && semverLt(item.installationInfo.version, attempt.target_version) + ); + extraLabelsBadges.push( + + + + + } + content={updateFailedAttempt ? formatAttempt(updateFailedAttempt) : undefined} + > + + + + + + + ); + } + + if (item.installationInfo?.install_status === installationStatuses.InstallFailed) { + const installFailedAttempt = item.installationInfo?.latest_install_failed_attempts?.find( + (attempt) => attempt.target_version === item.installationInfo?.version + ); + + extraLabelsBadges.push( + + + + + } + content={installFailedAttempt ? formatAttempt(installFailedAttempt) : undefined} + > + + + + + + + ); + } + + return extraLabelsBadges; +} + +function formatAttempt(attempt: InstallFailedAttempt): React.ReactNode { + return ( + <> + + + <> @ + + + ), + }} + /> +

    + {attempt.error?.name || ''} : {attempt.error?.message || ''} +

    + + ); +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx index b74ee96929968..2276d2df1924a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/category_facets.tsx @@ -22,6 +22,9 @@ export interface CategoryFacet { } export const UPDATES_AVAILABLE = 'updates_available'; +export const INSTALL_FAILED = 'install_failed'; +export const UPDATE_FAILED = 'update_failed'; + export type ExtendedIntegrationCategory = IntegrationCategory | typeof UPDATES_AVAILABLE | ''; export const ALL_CATEGORY = { @@ -45,6 +48,20 @@ export const UPDATES_AVAILABLE_CATEGORY = { }), }; +export const INSTALL_FAILED_CATEGORY = { + id: INSTALL_FAILED, + title: i18n.translate('xpack.fleet.epmList.installFailedFilterLinkText', { + defaultMessage: 'Install failed', + }), +}; + +export const UPDATE_FAILED_CATEGORY = { + id: UPDATE_FAILED, + title: i18n.translate('xpack.fleet.epmList.updateFailedFilterLinkText', { + defaultMessage: 'Update failed', + }), +}; + export interface Props { isLoading?: boolean; categories: CategoryFacet[]; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 78d919a565fa4..81dec69d0d911 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -8,26 +8,11 @@ import React, { useState, useMemo } from 'react'; import { Routes, Route } from '@kbn/shared-ux-router'; -import type { - CustomIntegration, - CustomIntegrationIcon, -} from '@kbn/custom-integrations-plugin/common'; - -import { hasDeferredInstallations } from '../../../../../../services/has_deferred_installations'; -import { getPackageReleaseLabel } from '../../../../../../../common/services'; - import { installationStatuses } from '../../../../../../../common/constants'; -import type { - PackageSpecIcon, - IntegrationCardReleaseLabel, -} from '../../../../../../../common/types'; -import type { DynamicPage, DynamicPagePathValues, StaticPage } from '../../../../constants'; import { INTEGRATIONS_ROUTING_PATHS, INTEGRATIONS_SEARCH_QUERYPARAM } from '../../../../constants'; import { DefaultLayout } from '../../../../layouts'; -import { isPackageUnverified, isPackageUpdatable } from '../../../../services'; - -import type { PackageListItem } from '../../../../types'; +import { isPackageUpdatable } from '../../../../services'; import { useGetPackagesQuery } from '../../../../hooks'; @@ -36,29 +21,13 @@ import type { CategoryFacet, ExtendedIntegrationCategory } from './category_face import { InstalledPackages } from './installed_packages'; import { AvailablePackages } from './available_packages'; +export { mapToCard, type IntegrationCardItem } from './card_utils'; + export interface CategoryParams { category?: ExtendedIntegrationCategory; subcategory?: string; } -export interface IntegrationCardItem { - url: string; - release?: IntegrationCardReleaseLabel; - description: string; - name: string; - title: string; - version: string; - icons: Array; - integration: string; - id: string; - categories: string[]; - fromIntegrations?: string; - isReauthorizationRequired?: boolean; - isUnverified?: boolean; - isUpdateAvailable?: boolean; - showLabels?: boolean; -} - export const getParams = (params: CategoryParams, search: string) => { const { category, subcategory } = params; const selectedCategory: ExtendedIntegrationCategory = category || ''; @@ -71,71 +40,6 @@ export const categoryExists = (category: string, categories: CategoryFacet[]) => return categories.some((c) => c.id === category); }; -export const mapToCard = ({ - getAbsolutePath, - getHref, - item, - addBasePath, - packageVerificationKeyId, - selectedCategory, -}: { - getAbsolutePath: (p: string) => string; - getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => string; - addBasePath: (url: string) => string; - item: CustomIntegration | PackageListItem; - packageVerificationKeyId?: string; - selectedCategory?: string; -}): IntegrationCardItem => { - let uiInternalPathUrl: string; - - let isUnverified = false; - - const version = 'version' in item ? item.version || '' : ''; - - let isUpdateAvailable = false; - let isReauthorizationRequired = false; - if (item.type === 'ui_link') { - uiInternalPathUrl = item.id.includes('language_client.') - ? addBasePath(item.uiInternalPath) - : item.uiExternalLink || getAbsolutePath(item.uiInternalPath); - } else { - let urlVersion = item.version; - if (item?.installationInfo?.version) { - urlVersion = item.installationInfo.version || item.version; - isUnverified = isPackageUnverified(item, packageVerificationKeyId); - isUpdateAvailable = isPackageUpdatable(item); - - isReauthorizationRequired = hasDeferredInstallations(item); - } - - const url = getHref('integration_details_overview', { - pkgkey: `${item.name}-${urlVersion}`, - ...(item.integration ? { integration: item.integration } : {}), - }); - - uiInternalPathUrl = url; - } - - const release: IntegrationCardReleaseLabel = getPackageReleaseLabel(version); - - return { - id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}:${item.id}`, - description: item.description, - icons: !item.icons || !item.icons.length ? [] : item.icons, - title: item.title, - url: uiInternalPathUrl, - fromIntegrations: selectedCategory, - integration: 'integration' in item ? item.integration || '' : '', - name: 'name' in item ? item.name : item.id, - version, - release, - categories: ((item.categories || []) as string[]).filter((c: string) => !!c), - isReauthorizationRequired, - isUnverified, - isUpdateAvailable, - }; -}; - export const EPMHomePage: React.FC = () => { const [prereleaseEnabled, setPrereleaseEnabled] = useState(false); @@ -145,7 +49,12 @@ export const EPMHomePage: React.FC = () => { }); const installedPackages = useMemo( - () => (allPackages?.items || []).filter((pkg) => pkg.status === installationStatuses.Installed), + () => + (allPackages?.items || []).filter( + (pkg) => + pkg.status === installationStatuses.Installed || + pkg.status === installationStatuses.InstallFailed + ), [allPackages] ); 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 bf916f231ca40..b4de5073a1674 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 @@ -21,12 +21,16 @@ import { useGetPackageVerificationKeyId, } from '../../../../hooks'; import { PackageListGrid } from '../../components/package_list_grid'; - +import { installationStatuses } from '../../../../../../../common/constants'; import type { PackageListItem } from '../../../../types'; import type { IntegrationsURLParameters } from './hooks/use_available_packages'; -import type { CategoryFacet, ExtendedIntegrationCategory } from './category_facets'; +import { + type CategoryFacet, + type ExtendedIntegrationCategory, + UPDATE_FAILED, +} from './category_facets'; import { CategoryFacets } from './category_facets'; import type { CategoryParams } from '.'; @@ -34,7 +38,10 @@ import { getParams, categoryExists, mapToCard } from '.'; import { ALL_INSTALLED_CATEGORY, UPDATES_AVAILABLE, + INSTALL_FAILED, UPDATES_AVAILABLE_CATEGORY, + UPDATE_FAILED_CATEGORY, + INSTALL_FAILED_CATEGORY, } from './category_facets'; const AnnouncementLink = () => { @@ -176,6 +183,26 @@ export const InstalledPackages: React.FC<{ [installedPackages] ); + // Todo move to another place + const installationFailedPackages = useMemo( + () => + installedPackages.filter( + (item) => item?.installationInfo?.install_status === installationStatuses.InstallFailed + ), + [installedPackages] + ); + + const updateFailedPackages = useMemo( + () => + installedPackages.filter((item) => + item?.installationInfo?.latest_install_failed_attempts?.some( + (attempt) => + item.installationInfo && semverLt(item.installationInfo.version, attempt.target_version) + ) + ), + [installedPackages] + ); + const categories: CategoryFacet[] = useMemo( () => [ { @@ -186,40 +213,61 @@ export const InstalledPackages: React.FC<{ ...UPDATES_AVAILABLE_CATEGORY, count: updatablePackages.length, }, + { + ...UPDATE_FAILED_CATEGORY, + count: updateFailedPackages.length, + }, + { + ...INSTALL_FAILED_CATEGORY, + count: installationFailedPackages.length, + }, ], - [installedPackages.length, updatablePackages.length] + [ + installedPackages.length, + updatablePackages.length, + installationFailedPackages.length, + updateFailedPackages.length, + ] ); + const cards = useMemo(() => { + let packages: PackageListItem[]; + if (selectedCategory === UPDATES_AVAILABLE) { + packages = updatablePackages; + } else if (selectedCategory === INSTALL_FAILED) { + packages = installationFailedPackages; + } else if (selectedCategory === UPDATE_FAILED) { + packages = updateFailedPackages; + } else { + packages = installedPackages; + } + return packages.map((item) => + mapToCard({ + getAbsolutePath, + getHref, + addBasePath, + item, + selectedCategory: selectedCategory || 'installed', + packageVerificationKeyId, + }) + ); + }, [ + selectedCategory, + updatablePackages, + installedPackages, + updateFailedPackages, + installationFailedPackages, + packageVerificationKeyId, + addBasePath, + getHref, + getAbsolutePath, + ]); + if (!categoryExists(selectedCategory, categories)) { setUrlandReplaceHistory({ searchString: searchTerm, categoryId: '' }); return null; } - const controls = ( - { - setCategory(id as ExtendedIntegrationCategory); - setSearchTerm(''); - setUrlandPushHistory({ searchString: '', categoryId: id }); - }} - /> - ); - - const cards = ( - selectedCategory === UPDATES_AVAILABLE ? updatablePackages : installedPackages - ).map((item) => - mapToCard({ - getAbsolutePath, - getHref, - addBasePath, - item, - selectedCategory: selectedCategory || 'installed', - packageVerificationKeyId, - }) - ); - let CalloutComponent = ; const unverifiedCount = cards.filter((c) => c.isUnverified).length; @@ -229,11 +277,22 @@ export const InstalledPackages: React.FC<{ } else if (updateAvailableCount) { CalloutComponent = ; } - const callout = selectedCategory === UPDATES_AVAILABLE || isLoading ? null : CalloutComponent; + const callout = selectedCategory !== '' || isLoading ? null : CalloutComponent; return ( { + setCategory(id as ExtendedIntegrationCategory); + setSearchTerm(''); + setUrlandPushHistory({ searchString: '', categoryId: id }); + }} + /> + } selectedCategory={selectedCategory} setCategory={setCategory} setUrlandPushHistory={setUrlandPushHistory} diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index edc34d50598ec..7bd380af258e2 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -34,6 +34,13 @@ import { PackagePolicyNotFoundError, FleetUnauthorizedError, PackagePolicyNameExistsError, + PackageOutdatedError, + PackageInvalidArchiveError, + BundledPackageLocationNotFoundError, + PackageRemovalError, + PackageESError, + KibanaSOReferenceError, + PackageAlreadyInstalledError, } from '.'; type IngestErrorHandler = ( @@ -47,30 +54,30 @@ interface IngestErrorHandlerParams { } // unsure if this is correct. would prefer to use something "official" // this type is based on BadRequest values observed while debugging https://github.com/elastic/kibana/issues/75862 - const getHTTPResponseCode = (error: FleetError): number => { - if (error instanceof RegistryResponseError) { - // 4xx/5xx's from EPR - return 500; + // Bad Request + if (error instanceof PackageFailedVerificationError) { + return 400; } - if (error instanceof RegistryConnectionError || error instanceof RegistryError) { - // Connection errors (ie. RegistryConnectionError) / fallback (RegistryError) from EPR - return 502; // Bad Gateway + if (error instanceof PackageOutdatedError) { + return 400; } - if (error instanceof PackageNotFoundError || error instanceof PackagePolicyNotFoundError) { - return 404; // Not Found + if (error instanceof PackageInvalidArchiveError) { + return 400; } - if (error instanceof AgentPolicyNameExistsError) { - return 409; // Conflict + if (error instanceof PackageRemovalError) { + return 400; } - if (error instanceof PackageUnsupportedMediaTypeError) { - return 415; // Unsupported Media Type + if (error instanceof KibanaSOReferenceError) { + return 400; } - if (error instanceof PackageFailedVerificationError) { - return 400; // Bad Request + // Unauthorized + if (error instanceof FleetUnauthorizedError) { + return 403; } - if (error instanceof ConcurrentInstallOperationError) { - return 409; // Conflict + // Not Found + if (error instanceof PackageNotFoundError || error instanceof PackagePolicyNotFoundError) { + return 404; } if (error instanceof AgentNotFoundError) { return 404; @@ -78,14 +85,41 @@ const getHTTPResponseCode = (error: FleetError): number => { if (error instanceof AgentActionNotFoundError) { return 404; } - if (error instanceof FleetUnauthorizedError) { - return 403; // Unauthorized + // Conflict + if (error instanceof AgentPolicyNameExistsError) { + return 409; + } + if (error instanceof ConcurrentInstallOperationError) { + return 409; } if (error instanceof PackagePolicyNameExistsError) { - return 409; // Conflict + return 409; + } + if (error instanceof PackageAlreadyInstalledError) { + return 409; + } + // Unsupported Media Type + if (error instanceof PackageUnsupportedMediaTypeError) { + return 415; } + // Internal Server Error if (error instanceof UninstallTokenError) { - return 500; // Internal Error + return 500; + } + if (error instanceof BundledPackageLocationNotFoundError) { + return 500; + } + if (error instanceof PackageESError) { + return 500; + } + if (error instanceof RegistryResponseError) { + // 4xx/5xx's from EPR + return 500; + } + // Bad Gateway + if (error instanceof RegistryConnectionError || error instanceof RegistryError) { + // Connection errors (ie. RegistryConnectionError) / fallback (RegistryError) from EPR + return 502; } return 400; // Bad Request }; @@ -115,7 +149,7 @@ export function fleetErrorToResponseOptions(error: IngestErrorHandlerParams['err }; } - // not sure what type of error this is. log as much as possible + // default response is 500 logger.error(error); return { statusCode: 500, diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 0b2c6b0fc5e93..7f607f4692774 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -26,8 +26,9 @@ export class RegistryResponseError extends RegistryError { super(message); } } + +// Package errors export class PackageNotFoundError extends FleetError {} -export class PackageKeyInvalidError extends FleetError {} export class PackageOutdatedError extends FleetError {} export class PackageFailedVerificationError extends FleetError { constructor(pkgName: string, pkgVersion: string) { @@ -37,22 +38,25 @@ export class PackageFailedVerificationError extends FleetError { }; } } +export class PackageUnsupportedMediaTypeError extends FleetError {} +export class PackageInvalidArchiveError extends FleetError {} +export class PackageRemovalError extends FleetError {} +export class PackageESError extends FleetError {} +export class ConcurrentInstallOperationError extends FleetError {} +export class BundledPackageLocationNotFoundError extends FleetError {} +export class KibanaSOReferenceError extends FleetError {} +export class PackageAlreadyInstalledError extends FleetError {} + export class AgentPolicyError extends FleetError {} export class AgentPolicyNotFoundError extends FleetError {} export class AgentNotFoundError extends FleetError {} export class AgentActionNotFoundError extends FleetError {} export class AgentPolicyNameExistsError extends AgentPolicyError {} -export class PackageUnsupportedMediaTypeError extends FleetError {} -export class PackageInvalidArchiveError extends FleetError {} -export class PackageCacheError extends FleetError {} -export class PackageOperationNotSupportedError extends FleetError {} -export class ConcurrentInstallOperationError extends FleetError {} export class AgentReassignmentError extends FleetError {} export class PackagePolicyIneligibleForUpgradeError extends FleetError {} export class PackagePolicyValidationError extends FleetError {} export class PackagePolicyNameExistsError extends FleetError {} export class PackagePolicyNotFoundError extends FleetError {} -export class BundledPackageNotFoundError extends FleetError {} export class HostedAgentPolicyRestrictionRelatedError extends FleetError { constructor(message = 'Cannot perform that action') { super( diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 2716fd82b6811..ec8ada164623d 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -201,5 +201,7 @@ export function createUninstallTokenServiceMock(): UninstallTokenServiceInterfac generateTokensForPolicyIds: jest.fn(), generateTokensForAllPolicies: jest.fn(), encryptTokens: jest.fn(), + checkTokenValidityForAllPolicies: jest.fn(), + checkTokenValidityForPolicy: jest.fn(), }; } diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts new file mode 100644 index 0000000000000..708d0e16a66fd --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts @@ -0,0 +1,241 @@ +/* + * Copyright 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 { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { Headers } from 'node-fetch'; + +import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_packages'; +import { getFile, getInstallation } from '../../services/epm/packages/get'; +import type { FleetRequestHandlerContext } from '../..'; +import { appContextService } from '../../services'; +import { unpackBufferEntries, getArchiveEntry } from '../../services/epm/archive'; +import { getAsset } from '../../services/epm/archive/storage'; + +import { getFileHandler } from './file_handler'; + +jest.mock('../../services/app_context'); +jest.mock('../../services/epm/archive'); +jest.mock('../../services/epm/archive/storage'); +jest.mock('../../services/epm/packages/bundled_packages'); +jest.mock('../../services/epm/packages/get'); + +const mockedGetBundledPackageByPkgKey = jest.mocked(getBundledPackageByPkgKey); +const mockedGetInstallation = jest.mocked(getInstallation); +const mockedGetFile = jest.mocked(getFile); +const mockedGetArchiveEntry = jest.mocked(getArchiveEntry); +const mockedUnpackBufferEntries = jest.mocked(unpackBufferEntries); +const mockedGetAsset = jest.mocked(getAsset); + +function mockContext() { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + return { + fleet: { + internalSOClient: async () => mockSavedObjectsClient, + }, + core: { + savedObjects: { + client: mockSavedObjectsClient, + }, + elasticsearch: { + client: { + asInternalUser: mockElasticsearchClient, + }, + }, + }, + } as unknown as FleetRequestHandlerContext; +} + +describe('getFileHandler', () => { + beforeEach(() => { + const logger = loggingSystemMock.createLogger(); + jest.mocked(appContextService).getLogger.mockReturnValue(logger); + mockedGetBundledPackageByPkgKey.mockReset(); + mockedUnpackBufferEntries.mockReset(); + mockedGetFile.mockReset(); + mockedGetInstallation.mockReset(); + mockedGetArchiveEntry.mockReset(); + mockedGetAsset.mockReset(); + }); + + it('should return the file for bundled package and an existing file', async () => { + mockedGetBundledPackageByPkgKey.mockResolvedValue({} as any); + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const buffer = Buffer.from(`TEST`); + mockedUnpackBufferEntries.mockResolvedValue([ + { + path: 'test-1.0.0/README.md', + buffer, + }, + ]); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + body: buffer, + headers: expect.objectContaining({ + 'content-type': 'text/markdown; charset=utf-8', + }), + }) + ); + }); + + it('should a 404 for bundled package with a non existing file', async () => { + mockedGetBundledPackageByPkgKey.mockResolvedValue({} as any); + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + mockedUnpackBufferEntries.mockResolvedValue([ + { + path: 'test-1.0.0/README.md', + buffer: Buffer.from(`TEST`), + }, + ]); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'bundled package file not found: idonotexists.md', + }) + ); + }); + + it('should proxy registry 200 for non bundled and non installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetFile.mockResolvedValue({ + status: 200, + // @ts-expect-error + body: 'test', + headers: new Headers({ + raw: '', + 'content-type': 'text/markdown', + }), + }); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + body: 'test', + headers: expect.objectContaining({ + 'content-type': 'text/markdown', + }), + }) + ); + }); + + it('should proxy registry 404 for non bundled and non installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetFile.mockResolvedValue({ + status: 404, + // @ts-expect-error + body: 'not found', + headers: new Headers({ + raw: '', + 'content-type': 'text', + }), + }); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'not found', + headers: expect.objectContaining({ + 'content-type': 'text', + }), + }) + ); + }); + + it('should return the file from installation for installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetInstallation.mockResolvedValue({ version: '1.0.0' } as any); + mockedGetArchiveEntry.mockReturnValue(Buffer.from('test')); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + headers: expect.objectContaining({ + 'content-type': 'text/markdown; charset=utf-8', + }), + }) + ); + }); + + it('should a 404 if the file from installation do not exists for installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetInstallation.mockResolvedValue({ version: '1.0.0' } as any); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'installed package file not found: README.md', + }) + ); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts new file mode 100644 index 0000000000000..b7572f1980cdc --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts @@ -0,0 +1,138 @@ +/* + * Copyright 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 path from 'path'; + +import type { TypeOf } from '@kbn/config-schema'; +import mime from 'mime-types'; +import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server'; + +import type { GetFileRequestSchema, FleetRequestHandler } from '../../types'; +import { getFile, getInstallation } from '../../services/epm/packages'; +import { defaultFleetErrorHandler } from '../../errors'; +import { getArchiveEntry } from '../../services/epm/archive'; +import { getAsset } from '../../services/epm/archive/storage'; +import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_packages'; +import { pkgToPkgKey } from '../../services/epm/registry'; +import { unpackBufferEntries } from '../../services/epm/archive'; + +const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { + 'cache-control': 'max-age=600', +}; +export const getFileHandler: FleetRequestHandler< + TypeOf +> = async (context, request, response) => { + try { + const { pkgName, pkgVersion, filePath } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; + + const installation = await getInstallation({ savedObjectsClient, pkgName }); + const useLocalFile = pkgVersion === installation?.version; + const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; + + if (useLocalFile) { + const fileBuffer = getArchiveEntry(assetPath); + // only pull local installation if we don't have it cached + const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); + + // error, if neither is available + if (!fileBuffer && !storedAsset) { + return response.custom({ + body: `installed package file not found: ${filePath}`, + statusCode: 404, + }); + } + + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = storedAsset + ? { + contentType: storedAsset.media_type, + buffer: storedAsset.data_utf8 + ? Buffer.from(storedAsset.data_utf8, 'utf8') + : Buffer.from(storedAsset.data_base64, 'base64'), + } + : { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); + } + + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } + + const bundledPackage = await getBundledPackageByPkgKey( + pkgToPkgKey({ name: pkgName, version: pkgVersion }) + ); + if (bundledPackage) { + const bufferEntries = await unpackBufferEntries(bundledPackage.buffer, 'application/zip'); + + const fileBuffer = bufferEntries.find((entry) => entry.path === assetPath)?.buffer; + + if (!fileBuffer) { + return response.custom({ + body: `bundled package file not found: ${filePath}`, + statusCode: 404, + }); + } + + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); + } + + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } else { + const registryResponse = await getFile(pkgName, pkgVersion, filePath); + const headersToProxy: KnownHeaders[] = ['content-type']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); + + return response.custom({ + body: registryResponse.body, + statusCode: registryResponse.status, + headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, + }); + } + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 8ce1bf7006f6b..6fadeff5180c2 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -5,12 +5,9 @@ * 2.0. */ -import path from 'path'; - import type { TypeOf } from '@kbn/config-schema'; -import mime from 'mime-types'; import semverValid from 'semver/functions/valid'; -import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server'; +import type { HttpResponseOptions } from '@kbn/core/server'; import { pick } from 'lodash'; @@ -41,7 +38,6 @@ import type { GetPackagesRequestSchema, GetInstalledPackagesRequestSchema, GetDataStreamsRequestSchema, - GetFileRequestSchema, GetInfoRequestSchema, InstallPackageFromRegistryRequestSchema, InstallPackageByUploadRequestSchema, @@ -60,21 +56,17 @@ import { getCategories, getPackages, getInstalledPackages, - getFile, getPackageInfo, isBulkInstallError, installPackage, removeInstallation, getLimitedPackages, - getInstallation, getBulkAssets, getTemplateInputs, } from '../../services/epm/packages'; import type { BulkInstallResponse } from '../../services/epm/packages'; import { defaultFleetErrorHandler, fleetErrorToResponseOptions, FleetError } from '../../errors'; import { appContextService, checkAllowedPackages } from '../../services'; -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'; @@ -206,80 +198,6 @@ export const getLimitedListHandler: FleetRequestHandler< } }; -export const getFileHandler: FleetRequestHandler< - TypeOf -> = async (context, request, response) => { - try { - const { pkgName, pkgVersion, filePath } = request.params; - const savedObjectsClient = (await context.fleet).internalSoClient; - const installation = await getInstallation({ savedObjectsClient, pkgName }); - const useLocalFile = pkgVersion === installation?.version; - - if (useLocalFile) { - const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; - const fileBuffer = getArchiveEntry(assetPath); - // only pull local installation if we don't have it cached - const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); - - // error, if neither is available - if (!fileBuffer && !storedAsset) { - return response.custom({ - body: `installed package file not found: ${filePath}`, - statusCode: 404, - }); - } - - // if storedAsset is not available, fileBuffer *must* be - // b/c we error if we don't have at least one, and storedAsset is the least likely - const { buffer, contentType } = storedAsset - ? { - contentType: storedAsset.media_type, - buffer: storedAsset.data_utf8 - ? Buffer.from(storedAsset.data_utf8, 'utf8') - : Buffer.from(storedAsset.data_base64, 'base64'), - } - : { - contentType: mime.contentType(path.extname(assetPath)), - buffer: fileBuffer, - }; - - if (!contentType) { - return response.custom({ - body: `unknown content type for file: ${filePath}`, - statusCode: 400, - }); - } - - return response.custom({ - body: buffer, - statusCode: 200, - headers: { - ...CACHE_CONTROL_10_MINUTES_HEADER, - 'content-type': contentType, - }, - }); - } else { - const registryResponse = await getFile(pkgName, pkgVersion, filePath); - const headersToProxy: KnownHeaders[] = ['content-type']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; - } - return headers; - }, {} as ResponseHeaders); - - return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, - }); - } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } -}; - export const getInfoHandler: FleetRequestHandler< TypeOf, TypeOf @@ -690,7 +608,9 @@ const soToInstallationInfo = (pkg: PackageListItem | PackageInfo) => { verification_status: attributes.verification_status, verification_key_id: attributes.verification_key_id, experimental_data_stream_features: attributes.experimental_data_stream_features, + latest_install_failed_attempts: attributes.latest_install_failed_attempts, }; + return { // When savedObject gets removed, replace `pkg` with `...omit(pkg, 'savedObject')` ...pkg, diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 6e0000bf4ccbf..5245381a409da 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -55,7 +55,6 @@ import { getListHandler, getInstalledListHandler, getLimitedListHandler, - getFileHandler, getInfoHandler, getBulkAssetsHandler, installPackageFromRegistryHandler, @@ -70,6 +69,7 @@ import { createCustomIntegrationHandler, getInputsHandler, } from './handlers'; +import { getFileHandler } from './file_handler'; const MAX_FILE_SIZE_BYTES = 104857600; // 100MB diff --git a/x-pack/plugins/fleet/server/routes/output/handler.test.ts b/x-pack/plugins/fleet/server/routes/output/handler.test.ts index 84443b3ad7196..5cf3b544e1553 100644 --- a/x-pack/plugins/fleet/server/routes/output/handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/output/handler.test.ts @@ -88,4 +88,41 @@ describe('output handler', () => { expect(res).toEqual({ body: { item: { id: 'output1' } } }); }); + + it('should return error if both service_token and secrets.service_token is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandler( + mockContext, + { + body: { + type: 'remote_elasticsearch', + service_token: 'token1', + secrets: { service_token: 'token2' }, + }, + } as any, + mockResponse as any + ); + + expect(res).toEqual({ + body: { message: 'Cannot specify both service_token and secrets.service_token' }, + statusCode: 400, + }); + }); + + it('should return ok if one of service_token and secrets.service_token is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandler( + mockContext, + { body: { type: 'remote_elasticsearch', secrets: { service_token: 'token2' } } } as any, + mockResponse as any + ); + + expect(res).toEqual({ body: { item: { id: 'output1' } } }); + }); }); diff --git a/x-pack/plugins/fleet/server/routes/output/handler.ts b/x-pack/plugins/fleet/server/routes/output/handler.ts index e100d9fd67e47..dc6682fe82727 100644 --- a/x-pack/plugins/fleet/server/routes/output/handler.ts +++ b/x-pack/plugins/fleet/server/routes/output/handler.ts @@ -37,9 +37,20 @@ function ensureNoDuplicateSecrets(output: Partial) { if (output.type === outputType.Kafka && output?.password && output?.secrets?.password) { throw Boom.badRequest('Cannot specify both password and secrets.password'); } - if (output.ssl?.key && output.secrets?.ssl?.key) { + if ( + (output.type === outputType.Kafka || output.type === outputType.Logstash) && + output.ssl?.key && + output.secrets?.ssl?.key + ) { throw Boom.badRequest('Cannot specify both ssl.key and secrets.ssl.key'); } + if ( + output.type === outputType.RemoteElasticsearch && + output.service_token && + output.secrets?.service_token + ) { + throw Boom.badRequest('Cannot specify both service_token and secrets.service_token'); + } } export const getOutputsHandler: RequestHandler = async (context, request, response) => { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index b87310e850c81..c470140d8ac23 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -270,6 +270,12 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, }, }, + service_token: { + dynamic: false, + properties: { + id: { type: 'keyword' }, + }, + }, }, }, }, @@ -304,6 +310,25 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, ], }, + '3': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + secrets: { + properties: { + service_token: { + dynamic: false, + properties: { + id: { type: 'keyword' }, + }, + }, + }, + }, + }, + }, + ], + }, }, migrations: { '7.13.0': migrateOutputToV7130, @@ -451,6 +476,7 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ deferred: { type: 'boolean' }, }, }, + latest_install_failed_attempts: { type: 'object', enabled: false }, installed_kibana: { dynamic: false, properties: {}, @@ -481,6 +507,18 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, }, }, + modelVersions: { + '1': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + latest_install_failed_attempts: { type: 'object', enabled: false }, + }, + }, + ], + }, + }, migrations: { '7.14.0': migrateInstallationToV7140, '7.14.1': migrateInstallationToV7140, diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 710ac46b94592..b6950ba672817 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -32,6 +32,7 @@ import { getFullAgentPolicy } from './agent_policies'; import * as outputsHelpers from './agent_policies/outputs_helpers'; import { auditLoggingService } from './audit_logging'; import { licenseService } from './license'; +import type { UninstallTokenServiceInterface } from './security/uninstall_token_service'; function getSavedObjectMock(agentPolicyAttributes: any) { const mock = savedObjectsClientMock.create(); @@ -182,13 +183,13 @@ describe('agent policy', () => { }); }); - it('should throw FleetUnauthorizedError if is_protected=true with insufficient license', () => { + it('should throw FleetUnauthorizedError if is_protected=true with insufficient license', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false); const soClient = getAgentPolicyCreateMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - expect( + await expect( agentPolicyService.create(soClient, esClient, { name: 'test', namespace: 'default', @@ -199,13 +200,13 @@ describe('agent policy', () => { ); }); - it('should not throw FleetUnauthorizedError if is_protected=false with insufficient license', () => { + it('should not throw FleetUnauthorizedError if is_protected=false with insufficient license', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false); const soClient = getAgentPolicyCreateMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - expect( + await expect( agentPolicyService.create(soClient, esClient, { name: 'test', namespace: 'default', @@ -619,7 +620,7 @@ describe('agent policy', () => { }); }); - it('should throw FleetUnauthorizedError if is_protected=true with insufficient license', () => { + it('should throw FleetUnauthorizedError if is_protected=true with insufficient license', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false); const soClient = getAgentPolicyCreateMock(); @@ -632,7 +633,7 @@ describe('agent policy', () => { references: [], }); - expect( + await expect( agentPolicyService.update(soClient, esClient, 'test-id', { name: 'test', namespace: 'default', @@ -643,7 +644,7 @@ describe('agent policy', () => { ); }); - it('should not throw FleetUnauthorizedError if is_protected=false with insufficient license', () => { + it('should not throw FleetUnauthorizedError if is_protected=false with insufficient license', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false); const soClient = getAgentPolicyCreateMock(); @@ -656,7 +657,7 @@ describe('agent policy', () => { references: [], }); - expect( + await expect( agentPolicyService.update(soClient, esClient, 'test-id', { name: 'test', namespace: 'default', @@ -665,6 +666,32 @@ describe('agent policy', () => { new FleetUnauthorizedError('Tamper protection requires Platinum license') ); }); + + it('should throw Error if is_protected=true with invalid uninstall token', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + + mockedAppContextService.getUninstallTokenService.mockReturnValueOnce({ + checkTokenValidityForPolicy: jest.fn().mockRejectedValueOnce(new Error('reason')), + } as unknown as UninstallTokenServiceInterface); + + const soClient = getAgentPolicyCreateMock(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + soClient.get.mockResolvedValue({ + attributes: {}, + id: 'test-id', + type: 'mocked', + references: [], + }); + + await expect( + agentPolicyService.update(soClient, esClient, 'test-id', { + name: 'test', + namespace: 'default', + is_protected: true, + }) + ).rejects.toThrowError(new Error('Cannot enable Agent Tamper Protection: reason')); + }); }); describe('deployPolicy', () => { diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 8673bd6ad91a9..5e8c897d5611a 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -512,6 +512,7 @@ class AgentPolicyService { } this.checkTamperProtectionLicense(agentPolicy); + await this.checkForValidUninstallToken(agentPolicy, id); const logger = appContextService.getLogger(); @@ -1212,6 +1213,20 @@ class AgentPolicyService { throw new FleetUnauthorizedError('Tamper protection requires Platinum license'); } } + private async checkForValidUninstallToken( + agentPolicy: { is_protected?: boolean }, + policyId: string + ): Promise { + if (agentPolicy?.is_protected) { + const uninstallTokenService = appContextService.getUninstallTokenService(); + + try { + await uninstallTokenService?.checkTokenValidityForPolicy(policyId); + } catch (e) { + throw new Error(`Cannot enable Agent Tamper Protection: ${e.message}`); + } + } + } } export const agentPolicyService = new AgentPolicyService(); diff --git a/x-pack/plugins/fleet/server/services/agent_policy_create.ts b/x-pack/plugins/fleet/server/services/agent_policy_create.ts index 11cb82123a347..9d1b3a9f01a5f 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy_create.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy_create.ts @@ -7,7 +7,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import type { HTTPAuthorizationHeader } from '../../common/http_authorization_header'; diff --git a/x-pack/plugins/fleet/server/services/agents/agent_metrics.ts b/x-pack/plugins/fleet/server/services/agents/agent_metrics.ts index 22fc64dad9e51..34cfbe547d6d4 100644 --- a/x-pack/plugins/fleet/server/services/agents/agent_metrics.ts +++ b/x-pack/plugins/fleet/server/services/agents/agent_metrics.ts @@ -11,6 +11,8 @@ import type { Agent } from '../../types'; import { appContextService } from '../app_context'; import { DATA_TIERS } from '../../../common/constants'; +const AGGREGATION_MAX_SIZE = 1000; + export async function fetchAndAssignAgentMetrics(esClient: ElasticsearchClient, agents: Agent[]) { try { return await _fetchAndAssignAgentMetrics(esClient, agents); @@ -112,6 +114,7 @@ const aggregationQueryBuilder = (agentIds: string[]) => ({ agents: { terms: { field: 'elastic_agent.id', + size: AGGREGATION_MAX_SIZE, }, aggs: { sum_memory_size: { @@ -127,6 +130,7 @@ const aggregationQueryBuilder = (agentIds: string[]) => ({ processes: { terms: { field: 'elastic_agent.process', + size: AGGREGATION_MAX_SIZE, order: { _count: 'desc', }, diff --git a/x-pack/plugins/fleet/server/services/agents/crud.test.ts b/x-pack/plugins/fleet/server/services/agents/crud.test.ts index e542881fe13f3..db08161c95190 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.test.ts @@ -151,7 +151,8 @@ describe('Agents CRUD test', () => { }); }); - describe('getAgentsByKuery', () => { + // FLAKY: https://github.com/elastic/kibana/issues/171541 + describe.skip('getAgentsByKuery', () => { it('should return upgradeable on first page', async () => { searchMock .mockImplementationOnce(() => Promise.resolve(getEsResponse(['1', '2', '3', '4', '5'], 7))) diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts index 8327e09842f8f..d847bf1aceb38 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.test.ts @@ -25,7 +25,11 @@ jest.mock('./action_status', () => { }; }); -describe('sendUpgradeAgentsActions (plural)', () => { +// FLAKY: https://github.com/elastic/kibana/issues/171052 +// FLAKY: https://github.com/elastic/kibana/issues/172114 +// FLAKY: https://github.com/elastic/kibana/issues/171536 +// FLAKY: https://github.com/elastic/kibana/issues/171160 +describe.skip('sendUpgradeAgentsActions (plural)', () => { beforeEach(async () => { appContextService.start(createAppContextStartContractMock()); }); diff --git a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts index 8bd0f823cb5c6..10d8c7560b269 100644 --- a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts +++ b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts @@ -368,7 +368,7 @@ spec: value: "1" # Set to true to communicate with Fleet with either insecure HTTP or unverified HTTPS - name: FLEET_INSECURE - value: "true" + value: "false" # Fleet Server URL to enroll the Elastic Agent into # FLEET_URL can be found in Kibana, go to Management > Fleet > Settings - name: FLEET_URL @@ -393,7 +393,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name - # The following ELASTIC_NETINFO:false variable will disable the netinfo.enabled option of add-host-metadata processor. This will remove fields host.ip and host.mac. + # The following ELASTIC_NETINFO:false variable will disable the netinfo.enabled option of add-host-metadata processor. This will remove fields host.ip and host.mac. # For more info: https://www.elastic.co/guide/en/beats/metricbeat/current/add-host-metadata.html - name: ELASTIC_NETINFO value: "false" diff --git a/x-pack/plugins/fleet/server/services/epm/agent/agent.ts b/x-pack/plugins/fleet/server/services/epm/agent/agent.ts index 077aa720b96d8..0bc220a500fb1 100644 --- a/x-pack/plugins/fleet/server/services/epm/agent/agent.ts +++ b/x-pack/plugins/fleet/server/services/epm/agent/agent.ts @@ -10,17 +10,18 @@ import { safeLoad, safeDump } from 'js-yaml'; import type { PackagePolicyConfigRecord } from '../../../../common/types'; import { toCompiledSecretRef } from '../../secrets'; +import { PackageInvalidArchiveError } from '../../../errors'; const handlebars = Handlebars.create(); export function compileTemplate(variables: PackagePolicyConfigRecord, templateStr: string) { - const { vars, yamlValues } = buildTemplateVariables(variables, templateStr); + const { vars, yamlValues } = buildTemplateVariables(variables); let compiledTemplate: string; try { const template = handlebars.compile(templateStr, { noEscape: true }); compiledTemplate = template(vars); } catch (err) { - throw new Error(`Error while compiling agent template: ${err.message}`); + throw new PackageInvalidArchiveError(`Error while compiling agent template: ${err.message}`); } compiledTemplate = replaceRootLevelYamlVariables(yamlValues, compiledTemplate); @@ -64,7 +65,7 @@ function replaceVariablesInYaml(yamlVariables: { [k: string]: any }, yaml: any) return yaml; } -function buildTemplateVariables(variables: PackagePolicyConfigRecord, templateStr: string) { +function buildTemplateVariables(variables: PackagePolicyConfigRecord) { const yamlValues: { [k: string]: any } = {}; const vars = Object.entries(variables).reduce((acc, [key, recordEntry]) => { // support variables with . like key.patterns @@ -72,13 +73,17 @@ function buildTemplateVariables(variables: PackagePolicyConfigRecord, templateSt const lastKeyPart = keyParts.pop(); if (!lastKeyPart || !isValidKey(lastKeyPart)) { - throw new Error('Invalid key'); + throw new PackageInvalidArchiveError( + `Error while compiling agent template: Invalid key ${lastKeyPart}` + ); } let varPart = acc; for (const keyPart of keyParts) { if (!isValidKey(keyPart)) { - throw new Error('Invalid key'); + throw new PackageInvalidArchiveError( + `Error while compiling agent template: Invalid key ${keyPart}` + ); } if (!varPart[keyPart]) { varPart[keyPart] = {}; diff --git a/x-pack/plugins/fleet/server/services/epm/archive/storage.ts b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts index cfa110589a010..81d55c5fd3138 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/storage.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/storage.ts @@ -19,6 +19,7 @@ import type { InstallSource, PackageAssetReference, } from '../../../../common/types'; +import { PackageInvalidArchiveError, PackageNotFoundError } from '../../../errors'; import { appContextService } from '../../app_context'; @@ -70,13 +71,13 @@ export async function archiveEntryToESDocument(opts: { // validation: filesize? asset type? anything else if (dataUtf8.length > currentMaxAssetBytes) { - throw new Error( + throw new PackageInvalidArchiveError( `File at ${path} is larger than maximum allowed size of ${currentMaxAssetBytes}` ); } if (dataBase64.length > currentMaxAssetBytes) { - throw new Error( + throw new PackageInvalidArchiveError( `After base64 encoding file at ${path} is larger than maximum allowed size of ${currentMaxAssetBytes}` ); } @@ -113,7 +114,7 @@ export async function saveArchiveEntries(opts: { const bulkBody = await Promise.all( paths.map((path) => { const buffer = getArchiveEntry(path); - if (!buffer) throw new Error(`Could not find ArchiveEntry at ${path}`); + if (!buffer) throw new PackageNotFoundError(`Could not find ArchiveEntry at ${path}`); const { name, version } = packageInfo; return archiveEntryToBulkCreateObject({ path, buffer, name, version, installSource }); }) diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts index 3aa86b526addd..61a75d28b7999 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ilm/install.ts @@ -14,6 +14,7 @@ import { getAsset, getPathParts } from '../../archive'; import { updateEsAssetReferences } from '../../packages/install'; import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; +import { PackageInvalidArchiveError } from '../../../../errors'; export async function installILMPolicy( packageInfo: InstallablePackage, @@ -57,7 +58,7 @@ export async function installILMPolicy( { logger } ); } catch (err) { - throw new Error(err.message); + throw new PackageInvalidArchiveError(`Couldn't install ilm policies: ${err.message}`); } }) ); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts index d65d8de5a3828..2e67c70874e4e 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts @@ -535,7 +535,7 @@ export function prepareTemplate({ const validFields = processFields(fields); - const mappings = generateMappings(validFields); + const mappings = generateMappings(validFields, isIndexModeTimeSeries); const templateName = generateTemplateName(dataStream); const templateIndexPattern = generateTemplateIndexPattern(dataStream); const templatePriority = getTemplatePriority(dataStream); 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 96a2547c9e58d..a621df33062a8 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 @@ -897,7 +897,30 @@ describe('EPM template', () => { }; const fields: Field[] = safeLoad(literalYml); const processedFields = processFields(fields); - const mappings = generateMappings(processedFields); + const mappings = generateMappings(processedFields, true); + expect(mappings).toEqual(expectedMapping); + }); + + it('tests processing dimension field on a keyword - tsdb disabled', () => { + const literalYml = ` +- name: example.id + type: keyword + dimension: true + `; + const expectedMapping = { + properties: { + example: { + properties: { + id: { + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(literalYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields, false); expect(mappings).toEqual(expectedMapping); }); @@ -921,7 +944,7 @@ describe('EPM template', () => { }; const fields: Field[] = safeLoad(literalYml); const processedFields = processFields(fields); - const mappings = generateMappings(processedFields); + const mappings = generateMappings(processedFields, true); expect(mappings).toEqual(expectedMapping); }); @@ -955,7 +978,40 @@ describe('EPM template', () => { }; const fields: Field[] = safeLoad(literalYml); const processedFields = processFields(fields); - const mappings = generateMappings(processedFields); + const mappings = generateMappings(processedFields, true); + expect(mappings).toEqual(expectedMapping); + }); + + it('tests processing metric_type field - tsdb disabled', () => { + const literalYml = ` +- name: total.norm.pct + type: scaled_float + metric_type: gauge + unit: percent + format: percent +`; + const expectedMapping = { + properties: { + total: { + properties: { + norm: { + properties: { + pct: { + scaling_factor: 1000, + type: 'scaled_float', + meta: { + unit: 'percent', + }, + }, + }, + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(literalYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields, false); expect(mappings).toEqual(expectedMapping); }); @@ -982,7 +1038,7 @@ describe('EPM template', () => { }; const fields: Field[] = safeLoad(literalYml); const processedFields = processFields(fields); - const mappings = generateMappings(processedFields); + const mappings = generateMappings(processedFields, true); expect(mappings).toEqual(expectedMapping); }); @@ -1292,7 +1348,7 @@ describe('EPM template', () => { }; const fields: Field[] = safeLoad(textWithRuntimeFieldsLiteralYml); const processedFields = processFields(fields); - const mappings = generateMappings(processedFields); + const mappings = generateMappings(processedFields, true); expect(mappings).toEqual(runtimeFieldMapping); }); 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 622538e152836..a26aa2c8e0114 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 @@ -33,6 +33,7 @@ import { } from '../../../../constants'; import { getESAssetMetadata } from '../meta'; import { retryTransientEsErrors } from '../retry'; +import { PackageESError, PackageInvalidArchiveError } from '../../../../errors'; import { getDefaultProperties, histogram, keyword, scaledFloat } from './mappings'; @@ -102,7 +103,9 @@ export function getTemplate({ isIndexModeTimeSeries, }); if (template.template.settings.index.final_pipeline) { - throw new Error(`Error template for ${templateIndexPattern} contains a final_pipeline`); + throw new PackageInvalidArchiveError( + `Error template for ${templateIndexPattern} contains a final_pipeline` + ); } const esBaseComponents = getBaseEsComponents(type, !!isIndexModeTimeSeries); @@ -141,46 +144,53 @@ const getBaseEsComponents = (type: string, isIndexModeTimeSeries: boolean): stri * * @param fields */ -export function generateMappings(fields: Field[]): IndexTemplateMappings { +export function generateMappings( + fields: Field[], + isIndexModeTimeSeries = false +): IndexTemplateMappings { const dynamicTemplates: Array> = []; const dynamicTemplateNames = new Set(); const runtimeFields: RuntimeFields = {}; - const { properties } = _generateMappings(fields, { - addDynamicMapping: (dynamicMapping: { - path: string; - matchingType: string; - pathMatch: string; - properties: Properties; - runtimeProperties?: Properties; - }) => { - const name = dynamicMapping.path; - if (dynamicTemplateNames.has(name)) { - return; - } + const { properties } = _generateMappings( + fields, + { + addDynamicMapping: (dynamicMapping: { + path: string; + matchingType: string; + pathMatch: string; + properties: Properties; + runtimeProperties?: Properties; + }) => { + const name = dynamicMapping.path; + if (dynamicTemplateNames.has(name)) { + return; + } - const dynamicTemplate: Properties = {}; - if (dynamicMapping.runtimeProperties !== undefined) { - dynamicTemplate.runtime = dynamicMapping.runtimeProperties; - } else { - dynamicTemplate.mapping = dynamicMapping.properties; - } + const dynamicTemplate: Properties = {}; + if (dynamicMapping.runtimeProperties !== undefined) { + dynamicTemplate.runtime = dynamicMapping.runtimeProperties; + } else { + dynamicTemplate.mapping = dynamicMapping.properties; + } - if (dynamicMapping.matchingType) { - dynamicTemplate.match_mapping_type = dynamicMapping.matchingType; - } + if (dynamicMapping.matchingType) { + dynamicTemplate.match_mapping_type = dynamicMapping.matchingType; + } - if (dynamicMapping.pathMatch) { - dynamicTemplate.path_match = dynamicMapping.pathMatch; - } + if (dynamicMapping.pathMatch) { + dynamicTemplate.path_match = dynamicMapping.pathMatch; + } - dynamicTemplateNames.add(name); - dynamicTemplates.push({ [dynamicMapping.path]: dynamicTemplate }); - }, - addRuntimeField: (runtimeField: { path: string; properties: Properties }) => { - runtimeFields[`${runtimeField.path}`] = runtimeField.properties; + dynamicTemplateNames.add(name); + dynamicTemplates.push({ [dynamicMapping.path]: dynamicTemplate }); + }, + addRuntimeField: (runtimeField: { path: string; properties: Properties }) => { + runtimeFields[`${runtimeField.path}`] = runtimeField.properties; + }, }, - }); + isIndexModeTimeSeries + ); const indexTemplateMappings: IndexTemplateMappings = { properties }; if (dynamicTemplates.length > 0) { @@ -206,7 +216,8 @@ function _generateMappings( addDynamicMapping: any; addRuntimeField: any; groupFieldName?: string; - } + }, + isIndexModeTimeSeries: boolean ): { properties: IndexTemplateMappings['properties']; hasNonDynamicTemplateMappings: boolean; @@ -294,7 +305,9 @@ function _generateMappings( case 'long': case 'boolean': dynProperties.type = field.object_type; - dynProperties.time_series_metric = field.metric_type; + if (isIndexModeTimeSeries) { + dynProperties.time_series_metric = field.metric_type; + } matchingType = field.object_type_mapping_type ?? field.object_type; default: break; @@ -359,7 +372,9 @@ function _generateMappings( case 'float': case 'half_float': dynProperties.type = field.object_type; - dynProperties.time_series_metric = field.metric_type; + if (isIndexModeTimeSeries) { + dynProperties.time_series_metric = field.metric_type; + } matchingType = field.object_type_mapping_type ?? 'double'; break; case 'byte': @@ -367,18 +382,24 @@ function _generateMappings( case 'short': case 'unsigned_long': dynProperties.type = field.object_type; - dynProperties.time_series_metric = field.metric_type; + if (isIndexModeTimeSeries) { + dynProperties.time_series_metric = field.metric_type; + } matchingType = field.object_type_mapping_type ?? 'long'; break; case 'integer': // Map integers as long, as in other cases. dynProperties.type = 'long'; - dynProperties.time_series_metric = field.metric_type; + if (isIndexModeTimeSeries) { + dynProperties.time_series_metric = field.metric_type; + } matchingType = field.object_type_mapping_type ?? 'long'; break; case 'boolean': dynProperties.type = field.object_type; - dynProperties.time_series_metric = field.metric_type; + if (isIndexModeTimeSeries) { + dynProperties.time_series_metric = field.metric_type; + } matchingType = field.object_type_mapping_type ?? field.object_type; break; case 'group': @@ -390,12 +411,16 @@ function _generateMappings( type: 'object', object_type: subField.object_type ?? subField.type, })); - const mappings = _generateMappings(subFields, { - ...ctx, - groupFieldName: ctx.groupFieldName - ? `${ctx.groupFieldName}.${field.name}` - : field.name, - }); + const mappings = _generateMappings( + subFields, + { + ...ctx, + groupFieldName: ctx.groupFieldName + ? `${ctx.groupFieldName}.${field.name}` + : field.name, + }, + isIndexModeTimeSeries + ); if (mappings.hasDynamicTemplateMappings) { hasDynamicTemplateMappings = true; } @@ -405,8 +430,8 @@ function _generateMappings( matchingType = field.object_type_mapping_type ?? 'object'; break; default: - throw new Error( - `no dynamic mapping generated for field ${path} of type ${field.object_type}` + throw new PackageInvalidArchiveError( + `No dynamic mapping generated for field ${path} of type ${field.object_type}` ); } @@ -422,12 +447,16 @@ function _generateMappings( switch (type) { case 'group': - const mappings = _generateMappings(field.fields!, { - ...ctx, - groupFieldName: ctx.groupFieldName - ? `${ctx.groupFieldName}.${field.name}` - : field.name, - }); + const mappings = _generateMappings( + field.fields!, + { + ...ctx, + groupFieldName: ctx.groupFieldName + ? `${ctx.groupFieldName}.${field.name}` + : field.name, + }, + isIndexModeTimeSeries + ); if (mappings.hasNonDynamicTemplateMappings) { fieldProps = { properties: @@ -450,12 +479,16 @@ function _generateMappings( break; case 'group-nested': fieldProps = { - properties: _generateMappings(field.fields!, { - ...ctx, - groupFieldName: ctx.groupFieldName - ? `${ctx.groupFieldName}.${field.name}` - : field.name, - }).properties, + properties: _generateMappings( + field.fields!, + { + ...ctx, + groupFieldName: ctx.groupFieldName + ? `${ctx.groupFieldName}.${field.name}` + : field.name, + }, + isIndexModeTimeSeries + ).properties, ...generateNestedProps(field), type: 'nested', }; @@ -543,10 +576,10 @@ function _generateMappings( } } - if ('metric_type' in field) { + if ('metric_type' in field && isIndexModeTimeSeries) { fieldProps.time_series_metric = field.metric_type; } - if (field.dimension) { + if (field.dimension && isIndexModeTimeSeries) { fieldProps.time_series_dimension = field.dimension; } @@ -878,7 +911,9 @@ const rolloverDataStream = (dataStreamName: string, esClient: ElasticsearchClien alias: dataStreamName, }); } catch (error) { - throw new Error(`cannot rollover data stream [${dataStreamName}] due to error: ${error}`); + throw new PackageESError( + `Cannot rollover data stream [${dataStreamName}] due to error: ${error}` + ); } }; @@ -1025,7 +1060,11 @@ const updateExistingDataStream = async ({ { logger } ); } catch (err) { - throw new Error(`could not update lifecycle settings for ${dataStreamName}: ${err.message}`); + // Check if this error can happen because of invalid settings; + // We are returning a 500 but in that case it should be a 400 instead + throw new PackageESError( + `Could not update lifecycle settings for ${dataStreamName}: ${err.message}` + ); } } @@ -1048,6 +1087,8 @@ const updateExistingDataStream = async ({ { logger } ); } catch (err) { - throw new Error(`could not update index template settings for ${dataStreamName}`); + // Same as above - Check if this error can happen because of invalid settings; + // We are returning a 500 but in that case it should be a 400 instead + throw new PackageESError(`Could not update index template settings for ${dataStreamName}`); } }; diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 20a0484c77a4a..23327a2253f86 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -35,6 +35,7 @@ import { savedObjectTypes } from '../../packages'; import { indexPatternTypes, getIndexPatternSavedObjects } from '../index_pattern/install'; import { saveKibanaAssetsRefs } from '../../packages/install'; import { deleteKibanaSavedObjectsAssets } from '../../packages/remove'; +import { KibanaSOReferenceError } from '../../../../errors'; import { withPackageSpan } from '../../packages/utils'; @@ -340,7 +341,7 @@ export async function installKibanaSavedObjects({ ); if (otherErrors?.length) { - throw new Error( + throw new KibanaSOReferenceError( `Encountered ${ otherErrors.length } errors creating saved objects: ${formatImportErrorsForLog(otherErrors)}` @@ -383,7 +384,7 @@ export async function installKibanaSavedObjects({ }); if (resolveErrors?.length) { - throw new Error( + throw new KibanaSOReferenceError( `Encountered ${ resolveErrors.length } errors resolving reference errors: ${formatImportErrorsForLog(resolveErrors)}` diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts index 39ca950af93db..e6f71cb7cb96c 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -29,7 +29,7 @@ import type { } from '../../types'; import type { FleetAuthzRouteConfig } from '../security/types'; import { checkSuperuser, getAuthzFromRequest, doesNotHaveRequiredFleetAuthz } from '../security'; -import { FleetUnauthorizedError } from '../../errors'; +import { FleetUnauthorizedError, FleetError } from '../../errors'; import { INSTALL_PACKAGES_AUTHZ, READ_PACKAGE_INFO_AUTHZ } from '../../routes/epm'; import { installTransforms, isTransform } from './elasticsearch/transform/install'; @@ -208,7 +208,7 @@ class PackageClientImpl implements PackageClient { const transformPaths = assetPaths.filter(isTransform); if (transformPaths.length !== assetPaths.length) { - throw new Error('reinstallEsAssets is currently only implemented for transform assets'); + throw new FleetError('reinstallEsAssets is currently only implemented for transform assets'); } if (transformPaths.length) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index 105331f54d2cf..d8a943fef5341 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -57,6 +57,7 @@ import { installIndexTemplatesAndPipelines, } from './install'; import { withPackageSpan } from './utils'; +import { clearLatestFailedAttempts } from './install_errors_helpers'; // this is only exported for testing // use a leading underscore to indicate it's not the supported path @@ -333,6 +334,10 @@ export async function _installPackage({ install_status: 'installed', package_assets: packageAssetRefs, install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, + latest_install_failed_attempts: clearLatestFailedAttempts( + pkgVersion, + installedPkg?.attributes.latest_install_failed_attempts ?? [] + ), }) ); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts index 7078761a4a583..92f9674656103 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts @@ -9,7 +9,7 @@ import fs from 'fs/promises'; import path from 'path'; import type { BundledPackage, Installation } from '../../../types'; -import { FleetError } from '../../../errors'; +import { BundledPackageLocationNotFoundError } from '../../../errors'; import { appContextService } from '../../app_context'; import { splitPkgKey, pkgToPkgKey } from '../registry'; @@ -19,7 +19,9 @@ export async function getBundledPackages(): Promise { const bundledPackageLocation = config?.developer?.bundledPackageLocation; if (!bundledPackageLocation) { - throw new FleetError('xpack.fleet.developer.bundledPackageLocation is not configured'); + throw new BundledPackageLocationNotFoundError( + 'xpack.fleet.developer.bundledPackageLocation is not configured' + ); } // If the bundled package directory is missing, we log a warning during setup, @@ -51,7 +53,7 @@ export async function getBundledPackages(): Promise { return result; } catch (err) { const logger = appContextService.getLogger(); - logger.debug(`Unable to read bundled packages from ${bundledPackageLocation}`); + logger.warn(`Unable to read bundled packages from ${bundledPackageLocation}`); return []; } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index 68a884fd5f198..a3ed5f62d7f1e 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -44,7 +44,6 @@ import type { } from '../../../../common/types'; import type { Installation, PackageInfo, PackagePolicySOAttributes } from '../../../types'; import { - FleetError, PackageFailedVerificationError, PackageNotFoundError, RegistryResponseError, @@ -575,6 +574,7 @@ export async function getPackageFromSource(options: { logger.debug(`retrieved installed package ${pkgName}-${pkgVersion}`); } catch (error) { if (error instanceof PackageFailedVerificationError) { + logger.error(`package ${pkgName}-${pkgVersion} failed verification`); throw error; } // treating this is a 404 as no status code returned @@ -600,7 +600,7 @@ export async function getPackageFromSource(options: { } } if (!res) { - throw new FleetError(`package info for ${pkgName}-${pkgVersion} does not exist`); + throw new PackageNotFoundError(`Package info for ${pkgName}-${pkgVersion} does not exist`); } return { paths: res.paths, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index f3e464d7a9f67..bc3e138b5619c 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -243,6 +243,7 @@ describe('install', () => { it('should send telemetry on install failure, async error', async () => { jest.mocked(install._installPackage).mockRejectedValue(new Error('error')); jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + await installPackage({ spaceId: DEFAULT_SPACE_ID, installSource: 'registry', 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 3a7fc78096159..2dbf1662d4364 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -57,11 +57,13 @@ import { DATASET_VAR_NAME, } from '../../../../common/constants'; import { - type FleetError, + FleetError, PackageOutdatedError, PackagePolicyValidationError, ConcurrentInstallOperationError, FleetUnauthorizedError, + PackageInvalidArchiveError, + PackageNotFoundError, } from '../../../errors'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import { dataStreamService, licenseService } from '../..'; @@ -104,6 +106,7 @@ import { cacheAssets } from './custom_integrations/assets/cache'; import { generateDatastreamEntries } from './custom_integrations/assets/dataset/utils'; import { checkForNamingCollision } from './custom_integrations/validation/check_naming_collision'; import { checkDatasetsNameFormat } from './custom_integrations/validation/check_dataset_name_format'; +import { addErrorToLatestFailedAttempts } from './install_errors_helpers'; export async function isPackageInstalled(options: { savedObjectsClient: SavedObjectsClientContract; @@ -202,7 +205,7 @@ export async function ensureInstalledPackage(options: { } const installation = await getInstallation({ savedObjectsClient, pkgName }); - if (!installation) throw new Error(`could not get installation ${pkgName}`); + if (!installation) throw new FleetError(`Could not get installation for ${pkgName}`); return installation; } @@ -234,22 +237,33 @@ export async function handleInstallPackageFailure({ version: pkgVersion, }); + const latestInstallFailedAttempts = addErrorToLatestFailedAttempts({ + error, + targetVersion: pkgVersion, + createdAt: new Date().toISOString(), + latestAttempts: installedPkg?.attributes.latest_install_failed_attempts, + }); + // if there is an unknown server error, uninstall any package assets or reinstall the previous version if update try { const installType = getInstallType({ pkgVersion, installedPkg }); - if (installType === 'install' || installType === 'reinstall') { + if (installType === 'install') { logger.error(`uninstalling ${pkgkey} after error installing: [${error.toString()}]`); await removeInstallation({ savedObjectsClient, pkgName, pkgVersion, esClient }); return; } - await updateInstallStatus({ savedObjectsClient, pkgName, status: 'install_failed' }).catch( - (err) => { - if (!SavedObjectsErrorHelpers.isNotFoundError(err)) { - logger.error(`failed to update package status to: install_failed ${err}`); - } - } - ); + await updateInstallStatusToFailed({ + logger, + savedObjectsClient, + pkgName, + status: 'install_failed', + latestInstallFailedAttempts, + }); + + if (installType === 'reinstall') { + logger.error(`Failed to reinstall ${pkgkey}: [${error.toString()}]`, { error }); + } if (installType === 'update') { if (!installedPkg) { @@ -272,13 +286,20 @@ export async function handleInstallPackageFailure({ } } catch (e) { // If an error happens while removing the integration or while doing a rollback update the status to failed - await updateInstallStatus({ savedObjectsClient, pkgName, status: 'install_failed' }).catch( - (err) => { - if (!SavedObjectsErrorHelpers.isNotFoundError(err)) { - logger.error(`failed to update package status to: install_failed ${err}`); - } - } - ); + await updateInstallStatusToFailed({ + logger, + savedObjectsClient, + pkgName, + status: 'install_failed', + latestInstallFailedAttempts: installedPkg + ? addErrorToLatestFailedAttempts({ + error: e, + targetVersion: installedPkg.attributes.version, + createdAt: installedPkg.attributes.install_started_at, + latestAttempts: latestInstallFailedAttempts, + }) + : [], + }); logger.error(`failed to uninstall or rollback package after installation error ${e}`); } } @@ -597,7 +618,9 @@ async function installPackageCommon(options: { return { assets, status: 'installed', installType, installSource }; }) .catch(async (err: Error) => { - logger.warn(`Failure to install package [${pkgName}]: [${err.toString()}]`); + logger.warn(`Failure to install package [${pkgName}]: [${err.toString()}]`, { + error: { stack_trace: err.stack }, + }); await handleInstallPackageFailure({ savedObjectsClient, error: err, @@ -712,7 +735,7 @@ export type InstallPackageParams = { export async function installPackage(args: InstallPackageParams): Promise { if (!('installSource' in args)) { - throw new Error('installSource is required'); + throw new FleetError('installSource is required'); } const logger = appContextService.getLogger(); @@ -803,7 +826,7 @@ export async function installPackage(args: InstallPackageParams): Promise { auditLoggingService.writeCustomSoAuditLog({ action: 'update', id: pkgName, savedObjectType: PACKAGES_SAVED_OBJECT_TYPE, }); - - return savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { - install_status: status, - }); + try { + return await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { + install_status: status, + latest_install_failed_attempts: latestInstallFailedAttempts, + }); + } catch (err) { + if (!SavedObjectsErrorHelpers.isNotFoundError(err)) { + logger.error(`failed to update package status to: install_failed ${err}`); + } + } }; export async function restartInstallation(options: { @@ -1273,7 +1306,7 @@ export async function installAssetsForInputPackagePolicy(opts: { if (pkgInfo.type !== 'input') return; const paths = await getArchiveFilelist(pkgInfo); - if (!paths) throw new Error('No paths found for '); + if (!paths) throw new PackageInvalidArchiveError(`No paths found for ${pkgInfo.name}`); const datasetName = packagePolicy.inputs[0].streams[0].vars?.[DATASET_VAR_NAME]?.value; const [dataStream] = getNormalizedDataStreams(pkgInfo, datasetName); @@ -1336,7 +1369,9 @@ export async function installAssetsForInputPackagePolicy(opts: { logger, }); if (!installedPkg) - throw new Error('Unable to find installed package while creating index templates'); + throw new PackageNotFoundError( + `Error while creating index templates: unable to find installed package ${pkgInfo.name}` + ); await installIndexTemplatesAndPipelines({ installedPkg, paths, @@ -1381,5 +1416,5 @@ export function getInstallType(args: NoPkgArgs | HasPkgArgs): OnlyInstall | NotI if (pkgVersion === lastStartedInstallVersion && pkgVersion !== currentPkgVersion) return 'reupdate'; if (pkgVersion !== lastStartedInstallVersion && pkgVersion !== currentPkgVersion) return 'update'; - throw new Error('unknown install type'); + throw new FleetError('Unknown install type'); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.test.ts new file mode 100644 index 0000000000000..264ce1cbd5503 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { InstallFailedAttempt } from '../../../types'; + +import { + clearLatestFailedAttempts, + addErrorToLatestFailedAttempts, +} from './install_errors_helpers'; + +const generateFailedAttempt = (version: string) => ({ + target_version: version, + created_at: new Date().toISOString(), + error: { + name: 'test', + message: 'test', + }, +}); + +const mapFailledAttempsToTargetVersion = (attemps: InstallFailedAttempt[]) => + attemps.map((attempt) => attempt.target_version); + +describe('Install error helpers', () => { + describe('clearLatestFailedAttempts', () => { + const previousFailedAttemps: InstallFailedAttempt[] = [ + generateFailedAttempt('0.1.0'), + generateFailedAttempt('0.2.0'), + ]; + it('should clear previous error on succesfull upgrade', () => { + const currentFailledAttemps = clearLatestFailedAttempts('0.2.0', previousFailedAttemps); + + expect(mapFailledAttempsToTargetVersion(currentFailledAttemps)).toEqual([]); + }); + + it('should not clear previous upgrade error on succesfull rollback', () => { + const currentFailledAttemps = clearLatestFailedAttempts('0.1.0', previousFailedAttemps); + + expect(mapFailledAttempsToTargetVersion(currentFailledAttemps)).toEqual(['0.2.0']); + }); + }); + + describe('addErrorToLatestFailedAttempts', () => { + it('should only keep 5 errors', () => { + const previousFailedAttemps: InstallFailedAttempt[] = [ + generateFailedAttempt('0.2.5'), + generateFailedAttempt('0.2.4'), + generateFailedAttempt('0.2.3'), + generateFailedAttempt('0.2.2'), + generateFailedAttempt('0.2.1'), + ]; + const currentFailledAttemps = addErrorToLatestFailedAttempts({ + targetVersion: '0.2.6', + createdAt: new Date().toISOString(), + error: new Error('new test'), + latestAttempts: previousFailedAttemps, + }); + + expect(mapFailledAttempsToTargetVersion(currentFailledAttemps)).toEqual([ + '0.2.6', + '0.2.5', + '0.2.4', + '0.2.3', + '0.2.2', + ]); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.ts b/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.ts new file mode 100644 index 0000000000000..1642acd7349b4 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/packages/install_errors_helpers.ts @@ -0,0 +1,44 @@ +/* + * Copyright 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 { lt } from 'semver'; + +import type { InstallFailedAttempt } from '../../../types'; + +const MAX_ATTEMPTS_TO_KEEP = 5; + +export function clearLatestFailedAttempts( + installedVersion: string, + latestAttempts: InstallFailedAttempt[] = [] +) { + return latestAttempts.filter((attempt) => lt(installedVersion, attempt.target_version)); +} + +export function addErrorToLatestFailedAttempts({ + error, + createdAt, + targetVersion, + latestAttempts = [], +}: { + createdAt: string; + targetVersion: string; + error: Error; + latestAttempts?: InstallFailedAttempt[]; +}): InstallFailedAttempt[] { + return [ + { + created_at: createdAt, + target_version: targetVersion, + error: { + name: error.name, + message: error.message, + stack: error.stack, + }, + }, + ...latestAttempts, + ].slice(0, MAX_ATTEMPTS_TO_KEEP); +} diff --git a/x-pack/plugins/fleet/server/services/epm/packages/reinstall.ts b/x-pack/plugins/fleet/server/services/epm/packages/reinstall.ts index d2672b6089819..cb2b91de252f7 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/reinstall.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/reinstall.ts @@ -11,6 +11,8 @@ import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; import type { Installation } from '../../../types'; import { pkgToPkgKey } from '../registry'; +import { PackageNotFoundError, PackageAlreadyInstalledError } from '../../../errors'; + import { getBundledPackageForInstallation } from './bundled_packages'; import { installPackage } from './install'; @@ -29,9 +31,11 @@ export async function reinstallPackageForInstallation({ const matchingBundledPackage = await getBundledPackageForInstallation(installation); if (!matchingBundledPackage) { if (installation.install_source === 'bundled') { - throw new Error(`Cannot reinstall: ${installation.name}, bundled package not found`); + throw new PackageNotFoundError( + `Cannot reinstall: ${installation.name}, bundled package not found` + ); } else { - throw new Error('Cannot reinstall an uploaded package'); + throw new PackageAlreadyInstalledError('Cannot reinstall an uploaded package'); } } } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.test.ts index f15043fe52923..badb1bc7c1f40 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.test.ts @@ -18,6 +18,7 @@ jest.mock('../..', () => { getLogger: jest.fn().mockReturnValue({ info: jest.fn(), error: jest.fn(), + warn: jest.fn(), }), }, packagePolicyService: { @@ -78,7 +79,7 @@ describe('removeInstallation', () => { force: false, }) ).rejects.toThrowError( - `unable to remove package with existing package policy(s) in use by agent(s)` + `Unable to remove package with existing package policy(s) in use by agent(s)` ); }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts index c65a4d165cf7f..ba9bace6a0dee 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/remove.ts @@ -7,8 +7,6 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import Boom from '@hapi/boom'; - import type { SavedObject } from '@kbn/core/server'; import { SavedObjectsClient } from '@kbn/core/server'; @@ -42,6 +40,7 @@ import { deleteIlms } from '../elasticsearch/datastream_ilm/remove'; import { removeArchiveEntries } from '../archive/storage'; import { auditLoggingService } from '../../audit_logging'; +import { FleetError, PackageRemovalError } from '../../../errors'; import { populatePackagePolicyAssignedAgentsCount } from '../../package_policies/populate_package_policy_assigned_agents_count'; @@ -56,7 +55,7 @@ export async function removeInstallation(options: { }): Promise { const { savedObjectsClient, pkgName, pkgVersion, esClient } = options; const installation = await getInstallation({ savedObjectsClient, pkgName }); - if (!installation) throw Boom.badRequest(`${pkgName} is not installed`); + if (!installation) throw new PackageRemovalError(`${pkgName} is not installed`); const { total, items } = await packagePolicyService.list(savedObjectsClient, { kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, @@ -72,17 +71,12 @@ export async function removeInstallation(options: { if (options.force || items.every((item) => (item.agents ?? 0) === 0)) { // delete package policies const ids = items.map((item) => item.id); - appContextService - .getLogger() - .info( - `deleting package policies of ${pkgName} package because not used by agents or force flag was enabled: ${ids}` - ); await packagePolicyService.delete(savedObjectsClient, esClient, ids, { force: options.force, }); } else { - throw Boom.badRequest( - `unable to remove package with existing package policy(s) in use by agent(s)` + throw new PackageRemovalError( + `Unable to remove package with existing package policy(s) in use by agent(s)` ); } } @@ -242,7 +236,7 @@ async function deleteIndexTemplate(esClient: ElasticsearchClient, name: string): try { await esClient.indices.deleteIndexTemplate({ name }, { ignore: [404] }); } catch { - throw new Error(`error deleting index template ${name}`); + throw new FleetError(`Error deleting index template ${name}`); } } } @@ -253,7 +247,7 @@ async function deleteComponentTemplate(esClient: ElasticsearchClient, name: stri try { await esClient.cluster.deleteComponentTemplate({ name }, { ignore: [404] }); } catch (error) { - throw new Error(`error deleting component template ${name}`); + throw new FleetError(`Error deleting component template ${name}`); } } } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/update.ts b/x-pack/plugins/fleet/server/services/epm/packages/update.ts index 3072dfed86636..72c43b6dc688a 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/update.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/update.ts @@ -12,7 +12,7 @@ import type { ExperimentalIndexingFeature } from '../../../../common/types'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import type { Installation, UpdatePackageRequestSchema } from '../../../types'; -import { FleetError } from '../../../errors'; +import { PackageNotFoundError } from '../../../errors'; import { auditLoggingService } from '../../audit_logging'; @@ -29,7 +29,7 @@ export async function updatePackage( const installedPackage = await getInstallationObject({ savedObjectsClient, pkgName }); if (!installedPackage) { - throw new FleetError(`package ${pkgName} is not installed`); + throw new PackageNotFoundError(`Error while updating package: ${pkgName} is not installed`); } auditLoggingService.writeCustomSoAuditLog({ diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 487faf55730bd..24c81dd023244 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -43,6 +43,7 @@ import { PackageNotFoundError, RegistryResponseError, PackageFailedVerificationError, + PackageUnsupportedMediaTypeError, } from '../../../errors'; import { getBundledPackageByName } from '../packages/bundled_packages'; @@ -364,8 +365,11 @@ export async function getPackage( function ensureContentType(archivePath: string) { const contentType = mime.lookup(archivePath); + if (!contentType) { - throw new Error(`Unknown compression format for '${archivePath}'. Please use .zip or .gz`); + throw new PackageUnsupportedMediaTypeError( + `Unknown compression format for '${archivePath}'. Please use .zip or .gz` + ); } return contentType; } diff --git a/x-pack/plugins/fleet/server/services/fleet_proxies.ts b/x-pack/plugins/fleet/server/services/fleet_proxies.ts index 604276e6d5880..cf45b90804c22 100644 --- a/x-pack/plugins/fleet/server/services/fleet_proxies.ts +++ b/x-pack/plugins/fleet/server/services/fleet_proxies.ts @@ -205,7 +205,7 @@ async function updateRelatedSavedObject( outputService.update(soClient, esClient, output.id, { ...omit(output, 'id'), proxy_id: null, - }); + } as Partial); }, { concurrency: 20 } ); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 0cb1099b58ebb..016026533e3c7 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -419,7 +419,12 @@ class OutputService { soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, output: NewOutput, - options?: { id?: string; fromPreconfiguration?: boolean; overwrite?: boolean } + options?: { + id?: string; + fromPreconfiguration?: boolean; + overwrite?: boolean; + secretHashes?: Record; + } ): Promise { const data: OutputSOAttributes = { ...omit(output, ['ssl', 'secrets']) }; if (output.type === outputType.RemoteElasticsearch) { @@ -555,6 +560,7 @@ class OutputService { const { output: outputWithSecrets } = await extractAndWriteOutputSecrets({ output, esClient, + secretHashes: output.is_preconfigured ? options?.secretHashes : undefined, }); if (outputWithSecrets.secrets) data.secrets = outputWithSecrets.secrets; @@ -716,7 +722,10 @@ class OutputService { esClient: ElasticsearchClient, id: string, data: Partial, - { fromPreconfiguration = false }: { fromPreconfiguration: boolean } = { + { + fromPreconfiguration = false, + secretHashes, + }: { fromPreconfiguration: boolean; secretHashes?: Record } = { fromPreconfiguration: false, } ) { @@ -747,6 +756,7 @@ class OutputService { oldOutput: originalOutput, outputUpdate: data, esClient, + secretHashes: data.is_preconfigured ? secretHashes : undefined, }); updateData.secrets = secretsRes.outputUpdate.secrets; diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 350909d3de0a7..2a76a96607789 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -255,10 +255,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( ); }); - apm.startTransaction( - 'fleet.preconfiguration.addPackagePolicies.improved.prReview.50', - 'fleet' - ); + const s = apm.startSpan('Add preconfigured package policies', 'preconfiguration'); await addPreconfiguredPolicyPackages( soClient, esClient, @@ -267,7 +264,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( defaultOutput, true ); - apm.endTransaction('fleet.preconfiguration.addPackagePolicies.improved.prReview.50'); + s?.end(); // Add the is_managed flag after configuring package policies to avoid errors if (shouldAddIsManagedFlag) { diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts index 1023e1bdb7b56..174b8051a9914 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.test.ts @@ -17,6 +17,7 @@ import { createOrUpdatePreconfiguredOutputs, cleanPreconfiguredOutputs, getPreconfiguredOutputFromConfig, + hashSecret, } from './outputs'; jest.mock('../agent_policy_update'); @@ -46,24 +47,70 @@ const spyAgentPolicyServicBumpAllAgentPoliciesForOutput = jest.spyOn( ); describe('output preconfiguration', () => { - beforeEach(() => { + beforeEach(async () => { mockedOutputService.create.mockReset(); mockedOutputService.update.mockReset(); mockedOutputService.delete.mockReset(); mockedOutputService.getDefaultDataOutputId.mockReset(); mockedOutputService.getDefaultESHosts.mockReturnValue(['http://default-es:9200']); + const keyHash = (await hashSecret('secretKey')) as string; + const passwordHash = (await hashSecret('secretPassword')) as string; + const serviceTokenHash = (await hashSecret('secretServiceToken')) as string; mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise => { return [ { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', // @ts-ignore type: 'elasticsearch', hosts: ['http://es.co:80'], is_preconfigured: true, }, + { + id: 'existing-logstash-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output 1', + type: 'logstash', + hosts: ['test:4343'], + is_preconfigured: true, + ssl: { + key: 'unsecureKey', + }, + }, + { + id: 'existing-logstash-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output With Secrets 1', + type: 'logstash', + hosts: ['test:4343'], + is_preconfigured: true, + secrets: { + ssl: { + key: { + id: '123', + hash: keyHash, + }, + }, + }, + }, + { + id: 'existing-logstash-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output With Secrets 2', + type: 'logstash', + hosts: ['test:4343'], + is_preconfigured: true, + secrets: { + ssl: { + key: 'secretKey', + }, + }, + }, { id: 'existing-kafka-output-1', is_default: false, @@ -73,11 +120,91 @@ describe('output preconfiguration', () => { type: 'kafka', hosts: ['kafka.co:80'], is_preconfigured: true, + password: 'unsecurePassword', + ssl: { + key: 'unsecureKey', + }, + }, + { + id: 'existing-kafka-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Kafka Output With Secrets 1', + type: 'kafka', + hosts: ['kafka.co:80'], + is_preconfigured: true, + secrets: { + password: { + id: '456', + hash: passwordHash, + }, + ssl: { + key: { + id: '789', + hash: keyHash, + }, + }, + }, + }, + { + id: 'existing-kafka-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Kafka Output With Secrets 2', + type: 'kafka', + hosts: ['kafka.co:80'], + is_preconfigured: true, + secrets: { + password: 'secretPassword', + ssl: { + key: 'secretKey', + }, + }, + }, + { + id: 'existing-remote-es-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output 1', + type: 'remote_elasticsearch', + hosts: ['https:es.co:80'], + is_preconfigured: true, + service_token: 'unsecureServiceToken', + }, + { + id: 'existing-remote-es-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output With Secrets 1', + type: 'remote_elasticsearch', + hosts: ['https:es.co:80'], + is_preconfigured: true, + secrets: { + service_token: { + id: '101112', + hash: serviceTokenHash, + }, + }, + }, + { + id: 'existing-remote-es-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output With Secrets 2', + type: 'remote_elasticsearch', + hosts: ['https:es.co:80'], + is_preconfigured: true, + secrets: { + service_token: 'secretServiceToken', + }, }, ]; }); + spyAgentPolicyServicBumpAllAgentPoliciesForOutput.mockClear(); }); + // Output creation + it('should generate a preconfigured output if elasticsearch.hosts is set in the config', async () => { expect( getPreconfiguredOutputFromConfig({ @@ -104,13 +231,13 @@ describe('output preconfiguration', () => { `); }); - it('should create preconfigured output that does not exists', async () => { + it('should create a preconfigured ES output that does not exist', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'non-existing-output-1', - name: 'Output 1', + id: 'non-existing-es-output-1', + name: 'ES Output 1', type: 'elasticsearch', is_default: false, is_default_monitoring: false, @@ -123,37 +250,86 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); - it('should create preconfigured kafka output that does not exists', async () => { + it('should create a preconfigured ES output with ca_trusted_fingerprint that does not exist', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'non-existing-kafka-output-1', - name: 'Output 1', - type: 'kafka', + id: 'non-existing-es-output-1', + name: 'ES Output 1', + type: 'elasticsearch', is_default: false, is_default_monitoring: false, - hosts: ['test.fr:2000'], + hosts: ['http://test.fr'], + ca_trusted_fingerprint: 'testfingerprint', }, ]); expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.create).toBeCalledWith( + expect.anything(), + expect.anything(), + expect.objectContaining({ + ca_trusted_fingerprint: 'testfingerprint', + }), + expect.anything() + ); expect(mockedOutputService.update).not.toBeCalled(); expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); - it('should create a preconfigured output with ca_trusted_fingerprint that does not exists', async () => { + it('should create a preconfigured ES output with default hosts if the output does not exist and hosts is not set', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'non-existing-output-1', + id: 'non-existing-es-output-1', name: 'Output 1', type: 'elasticsearch', is_default: false, is_default_monitoring: false, - hosts: ['http://test.fr'], - ca_trusted_fingerprint: 'testfingerprint', + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.create.mock.calls[0][2].hosts).toEqual(['http://default-es:9200']); + }); + + it('should create a preconfigured logstash output that does not exist', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-logstash-output-1', + name: 'Logstash Output 1', + type: 'logstash', + is_default: false, + is_default_monitoring: false, + hosts: ['test:4343'], + ssl: { certificate: 'test', key: 'test' }, + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should create a preconfigured logstash output with secrets that does not exist', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-logstash-output-with-secrets-1', + name: 'Logstash Output With Secrets 1', + type: 'logstash', + is_default: false, + is_default_monitoring: false, + secrets: { + ssl: { + key: 'secretKey', + }, + }, }, ]); @@ -162,7 +338,11 @@ describe('output preconfiguration', () => { expect.anything(), expect.anything(), expect.objectContaining({ - ca_trusted_fingerprint: 'testfingerprint', + secrets: { + ssl: { + key: 'secretKey', + }, + }, }), expect.anything() ); @@ -170,18 +350,17 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); - it('should create preconfigured logstash output that does not exist', async () => { + it('should create preconfigured kafka output that does not exist', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'non-existing-output-1', - name: 'Output 1', - type: 'logstash', + id: 'non-existing-kafka-output-1', + name: 'Kafka Output 1', + type: 'kafka', is_default: false, is_default_monitoring: false, - hosts: ['test.fr'], - ssl: { certificate: 'test', key: 'test' }, + hosts: ['test.fr:2000'], }, ]); @@ -190,33 +369,83 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); - it('should set default hosts if hosts is not set output that does not exists', async () => { + it('should create a preconfigured kafka output with secrets that does not exist', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'non-existing-output-1', - name: 'Output 1', - type: 'elasticsearch', + id: 'non-existing-kafka-output-with-secrets-1', + name: 'Kafka Output With Secrets 2', + type: 'kafka', is_default: false, is_default_monitoring: false, + secrets: { + password: 'secretPassword', + ssl: { + key: 'secretKey', + }, + }, }, ]); expect(mockedOutputService.create).toBeCalled(); - expect(mockedOutputService.create.mock.calls[0][2].hosts).toEqual(['http://default-es:9200']); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should create a preconfigured remote ES output that does not exist', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-remote-es-output-1', + name: 'Remote ES Output 1', + type: 'remote_elasticsearch', + is_default: false, + is_default_monitoring: false, + hosts: ['test.fr'], + service_token: 'serviceToken', + } as PreconfiguredOutput, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should create a preconfigured remote ES output with secrets that does not exist', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-remote-es-output-with-secrets-1', + name: 'Remote ES Output With Secrets 2', + type: 'remote_elasticsearch', + is_default: false, + is_default_monitoring: false, + secrets: { + remote_elasticsearch: 'secretServiceToken', + }, + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); - it('should update output if non preconfigured output with the same id exists', async () => { + // Output should update + + it('should update output if non preconfigured ES output with the same id exists', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); mockedOutputService.bulkGet.mockResolvedValue([ { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', // @ts-ignore type: 'elasticsearch', hosts: ['http://es.co:80'], @@ -225,10 +454,10 @@ describe('output preconfiguration', () => { ]); await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', type: 'elasticsearch', hosts: ['http://es.co:80'], }, @@ -239,7 +468,7 @@ describe('output preconfiguration', () => { expect(mockedOutputService.update).toBeCalledWith( expect.anything(), expect.anything(), - 'existing-output-1', + 'existing-es-output-1', expect.objectContaining({ is_preconfigured: true, }), @@ -248,16 +477,16 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); }); - it('should update output if preconfigured output exists and changed', async () => { + it('should update output if preconfigured ES output exists and changed', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', type: 'elasticsearch', hosts: ['http://newhostichanged.co:9201'], // field that changed }, @@ -268,6 +497,54 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); }); + it('should update output if a preconfigured logstash ouput exists and has changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-logstash-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output 1', + type: 'logstash', + hosts: ['test:4343'], + is_preconfigured: true, + ssl: { + key: 'unsecureKey2', // field that changed + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + it('should update output if a preconfigured logstash ouput with secrets exists and has changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-logstash-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output With Secrets 1', + type: 'logstash', + secrets: { + ssl: { + key: 'secretKey2', // field that changed + }, + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + it('should update output if preconfigured kafka output exists and changed', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; @@ -279,7 +556,7 @@ describe('output preconfiguration', () => { is_default_monitoring: false, name: 'Kafka Output 1', type: 'kafka', - hosts: ['kafka.co:8080'], + hosts: ['kafka.co:8080'], // field that changed }, ]); @@ -288,18 +565,92 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); }); - it('should not update output if preconfigured output exists and did not changed', async () => { + it('should update ouput if a preconfigured kafka with secrets exists and has changed', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ { - id: 'existing-output-1', + id: 'existing-kafka-output-with-secrets-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', - type: 'elasticsearch', - hosts: ['http://newhostichanged.co:9201'], // field that changed + name: 'Kafka Output With Secrets 1', + type: 'kafka', + secrets: { + password: 'secretPassword2', // field that changed + ssl: { + key: 'secretKey2', + }, + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + it('should update output if preconfigured remote ES output exists and changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-remote-es-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output 1', + type: 'remote_elasticsearch', + is_preconfigured: true, + service_token: 'stillUnsecureServiceToken', // field that changed + } as PreconfiguredOutput, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + it('should update ouput if a preconfigured remote ES with secrets exists and has changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-remote-es-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output With Secrets 1', + type: 'remote_elasticsearch', + is_preconfigured: true, + secrets: { + service_token: 'secretServiceToken2', // field that changed + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + it('should update output if a preconfigured logstash output with plain value secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-logstash-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output With Secrets 2', + type: 'logstash', + hosts: ['test:4343'], + secrets: { + ssl: { + key: 'secretKey', // no change + }, + }, }, ]); @@ -308,6 +659,101 @@ describe('output preconfiguration', () => { expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); }); + it('should update output if a preconfigured kafka output with plain value secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-kafka-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Kafka Output With Secrets 2', + type: 'kafka', + hosts: ['kafka.co:80'], + secrets: { + password: 'secretPassword', // no change + ssl: { + key: 'secretKey', // no change + }, + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + it('should update output if a preconfigured remote ES output with plain value secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-remote-es-output-with-secrets-2', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output With Secrets 2', + type: 'remote_elasticsearch', + is_preconfigured: true, + secrets: { + service_token: 'secretServiceToken', // no change + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + // Output should not update + + it('should not update output if preconfigured ES output exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-es-output-1', + is_default: false, + is_default_monitoring: false, + name: 'ES Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:80'], + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should not update output if preconfigured logstash output exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-logstash-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output 1', + type: 'logstash', + hosts: ['test:4343'], + is_preconfigured: true, + ssl: { + key: 'unsecureKey', + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + it('should not update output if preconfigured kafka output exists and did not change', async () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; @@ -319,23 +765,124 @@ describe('output preconfiguration', () => { is_default_monitoring: false, name: 'Kafka Output 1', type: 'kafka', - hosts: ['kafka.co:8080'], + hosts: ['kafka.co:80'], + password: 'unsecurePassword', + ssl: { + key: 'unsecureKey', + }, + } as PreconfiguredOutput, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should not update output if preconfigured remote ES output exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-remote-es-output-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output 1', + type: 'remote_elasticsearch', + hosts: ['https:es.co:80'], + is_preconfigured: true, + service_token: 'unsecureServiceToken', + } as PreconfiguredOutput, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should not update output if a preconfigured logstash output with secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-logstash-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Logstash Output With Secrets 1', + type: 'logstash', + hosts: ['test:4343'], + secrets: { + ssl: { + key: 'secretKey', + }, + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should not update output if a preconfigured kafka output with secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-kafka-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Kafka Output With Secrets 1', + type: 'kafka', + hosts: ['kafka.co:80'], + secrets: { + password: 'secretPassword', + ssl: { + key: 'secretKey', + }, + }, }, ]); expect(mockedOutputService.create).not.toBeCalled(); - expect(mockedOutputService.update).toBeCalled(); - expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should not update output if a preconfigured remote ES output with secrets exists and did not change', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await createOrUpdatePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-remote-es-output-with-secrets-1', + is_default: false, + is_default_monitoring: false, + name: 'Remote ES Output With Secrets 1', + type: 'remote_elasticsearch', + hosts: ['https:es.co:80'], + is_preconfigured: true, + secrets: { + service_token: 'secretServiceToken', + }, + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); }); const SCENARIOS: Array<{ name: string; data: PreconfiguredOutput }> = [ { name: 'no changes', data: { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', type: 'elasticsearch', hosts: ['http://es.co:80'], }, @@ -343,10 +890,10 @@ describe('output preconfiguration', () => { { name: 'hosts without port', data: { - id: 'existing-output-1', + id: 'existing-es-output-1', is_default: false, is_default_monitoring: false, - name: 'Output 1', + name: 'ES Output 1', type: 'elasticsearch', hosts: ['http://es.co'], }, diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts index 5bc7c452b481e..ac105fc29a355 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts @@ -5,11 +5,20 @@ * 2.0. */ +import crypto from 'crypto'; + import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import { isEqual } from 'lodash'; import { safeDump } from 'js-yaml'; -import type { PreconfiguredOutput, Output, NewOutput } from '../../../common/types'; +import type { + PreconfiguredOutput, + Output, + NewOutput, + OutputSecret, + KafkaOutput, + NewLogstashOutput, +} from '../../../common/types'; import { normalizeHostsForAgents } from '../../../common/services'; import type { FleetConfigType } from '../../config'; import { DEFAULT_OUTPUT_ID, DEFAULT_OUTPUT } from '../../constants'; @@ -82,7 +91,7 @@ export async function createOrUpdatePreconfiguredOutputs( ca_sha256: outputData.ca_sha256 ?? null, ca_trusted_fingerprint: outputData.ca_trusted_fingerprint ?? null, ssl: outputData.ssl ?? null, - }; + } as NewOutput; if (!data.hosts || data.hosts.length === 0) { data.hosts = outputService.getDefaultESHosts(); @@ -99,25 +108,101 @@ export async function createOrUpdatePreconfiguredOutputs( } const isUpdateWithNewData = - existingOutput && isPreconfiguredOutputDifferentFromCurrent(existingOutput, data); - - if (isCreate) { - logger.debug(`Creating output ${output.id}`); - await outputService.create(soClient, esClient, data, { id, fromPreconfiguration: true }); - } else if (isUpdateWithNewData) { - logger.debug(`Updating output ${output.id}`); - await outputService.update(soClient, esClient, id, data, { fromPreconfiguration: true }); - // Bump revision of all policies using that output - if (outputData.is_default || outputData.is_default_monitoring) { - await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); - } else { - await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id); + existingOutput && (await isPreconfiguredOutputDifferentFromCurrent(existingOutput, data)); + + if (isCreate || isUpdateWithNewData) { + const secretHashes = await hashSecrets(output); + + if (isCreate) { + logger.debug(`Creating preconfigured output ${output.id}`); + await outputService.create(soClient, esClient, data, { + id, + fromPreconfiguration: true, + secretHashes, + }); + } else if (isUpdateWithNewData) { + logger.debug(`Updating preconfigured output ${output.id}`); + await outputService.update(soClient, esClient, id, data, { + fromPreconfiguration: true, + secretHashes, + }); + // Bump revision of all policies using that output + if (outputData.is_default || outputData.is_default_monitoring) { + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); + } else { + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id); + } } } }) ); } +// Values recommended by NodeJS documentation +const keyLength = 64; +const saltLength = 16; + +// N=2^14 (16 MiB), r=8 (1024 bytes), p=5 +const scryptParams = { + cost: 16384, + blockSize: 8, + parallelization: 5, +}; + +export async function hashSecret(secret: string) { + return new Promise((resolve, reject) => { + const salt = crypto.randomBytes(saltLength).toString('hex'); + crypto.scrypt(secret, salt, keyLength, scryptParams, (err, derivedKey) => { + if (err) reject(err); + resolve(`${salt}:${derivedKey.toString('hex')}`); + }); + }); +} + +async function verifySecret(hash: string, secret: string) { + return new Promise((resolve, reject) => { + const [salt, key] = hash.split(':'); + crypto.scrypt(secret, salt, keyLength, scryptParams, (err, derivedKey) => { + if (err) reject(err); + resolve(crypto.timingSafeEqual(Buffer.from(key, 'hex'), derivedKey)); + }); + }); +} + +async function hashSecrets(output: PreconfiguredOutput) { + if (output.type === 'kafka') { + const kafkaOutput = output as KafkaOutput; + if (typeof kafkaOutput.secrets?.password === 'string') { + const password = await hashSecret(kafkaOutput.secrets?.password); + return { + password, + }; + } + if (typeof kafkaOutput.secrets?.ssl?.key === 'string') { + const key = await hashSecret(kafkaOutput.secrets?.ssl?.key); + return { + ssl: { + key, + }, + }; + } + } + if (output.type === 'logstash') { + const logstashOutput = output as NewLogstashOutput; + + if (typeof logstashOutput.secrets?.ssl?.key === 'string') { + const key = await hashSecret(logstashOutput.secrets?.ssl?.key); + return { + ssl: { + key, + }, + }; + } + } + + return undefined; +} + export async function cleanPreconfiguredOutputs( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -165,15 +250,56 @@ export async function cleanPreconfiguredOutputs( } } -function isPreconfiguredOutputDifferentFromCurrent( +const hasHash = (secret?: OutputSecret): secret is { id: string; hash: string } => { + return !!secret && typeof secret !== 'string' && !!secret.hash; +}; + +async function isSecretDifferent( + preconfiguredValue: OutputSecret | undefined, + existingSecret: OutputSecret | undefined +): Promise { + if (!existingSecret && preconfiguredValue) { + return true; + } + + if (!preconfiguredValue && existingSecret) { + return true; + } + + if (!preconfiguredValue && !existingSecret) { + return false; + } + + if (hasHash(existingSecret) && typeof preconfiguredValue === 'string') { + // verifying the has tells us if the value has changed + const hashIsVerified = await verifySecret(existingSecret.hash, preconfiguredValue!); + + return !hashIsVerified; + } else { + // if there is no hash then the safest thing to do is assume the value has changed + return true; + } +} + +async function isPreconfiguredOutputDifferentFromCurrent( existingOutput: Output, preconfiguredOutput: Partial -): boolean { - const kafkaFieldsAreDifferent = (): boolean => { +): Promise { + const kafkaFieldsAreDifferent = async (): Promise => { if (existingOutput.type !== 'kafka' || preconfiguredOutput.type !== 'kafka') { return false; } + const passwordHashIsDifferent = await isSecretDifferent( + preconfiguredOutput.secrets?.password, + existingOutput.secrets?.password + ); + + const sslKeyHashIsDifferent = await isSecretDifferent( + preconfiguredOutput.secrets?.ssl?.key, + existingOutput.secrets?.ssl?.key + ); + return ( isDifferent(existingOutput.client_id, preconfiguredOutput.client_id) || isDifferent(existingOutput.version, preconfiguredOutput.version) || @@ -193,8 +319,22 @@ function isPreconfiguredOutputDifferentFromCurrent( isDifferent(existingOutput.headers, preconfiguredOutput.headers) || isDifferent(existingOutput.timeout, preconfiguredOutput.timeout) || isDifferent(existingOutput.broker_timeout, preconfiguredOutput.broker_timeout) || - isDifferent(existingOutput.required_acks, preconfiguredOutput.required_acks) + isDifferent(existingOutput.required_acks, preconfiguredOutput.required_acks) || + passwordHashIsDifferent || + sslKeyHashIsDifferent + ); + }; + + const logstashFieldsAreDifferent = async (): Promise => { + if (existingOutput.type !== 'logstash' || preconfiguredOutput.type !== 'logstash') { + return false; + } + const sslKeyHashIsDifferent = await isSecretDifferent( + preconfiguredOutput.secrets?.ssl?.key, + existingOutput.secrets?.ssl?.key ); + + return sslKeyHashIsDifferent; }; return ( @@ -221,6 +361,7 @@ function isPreconfiguredOutputDifferentFromCurrent( isDifferent(existingOutput.config_yaml, preconfiguredOutput.config_yaml) || isDifferent(existingOutput.proxy_id, preconfiguredOutput.proxy_id) || isDifferent(existingOutput.allow_edit ?? [], preconfiguredOutput.allow_edit ?? []) || - kafkaFieldsAreDifferent() + (await kafkaFieldsAreDifferent()) || + (await logstashFieldsAreDifferent()) ); } diff --git a/x-pack/plugins/fleet/server/services/secrets.test.ts b/x-pack/plugins/fleet/server/services/secrets.test.ts index 66b13638085d3..3a46d90f557ec 100644 --- a/x-pack/plugins/fleet/server/services/secrets.test.ts +++ b/x-pack/plugins/fleet/server/services/secrets.test.ts @@ -12,584 +12,918 @@ * 2[0]. */ +import { v4 as uuidv4 } from 'uuid'; + +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; + +import { createAppContextStartContractMock } from '../mocks'; + import type { NewPackagePolicy, PackageInfo } from '../types'; -import { getPolicySecretPaths, diffSecretPaths } from './secrets'; +import { appContextService } from './app_context'; +import { + getPolicySecretPaths, + diffSecretPaths, + diffOutputSecretPaths, + extractAndWriteSecrets, +} from './secrets'; -describe('getPolicySecretPaths', () => { - describe('integration package with one policy template', () => { - const mockIntegrationPackage = { - name: 'mock-package', - title: 'Mock package', - version: '0[0].0', - description: 'description', - type: 'integration', - status: 'not_installed', - vars: [ - { name: 'pkg-secret-1', type: 'text', secret: true }, - { name: 'pkg-secret-2', type: 'text', secret: true }, - ], - data_streams: [ - { - dataset: 'somedataset', - streams: [ +describe('secrets', () => { + let mockContract: ReturnType; + + beforeEach(async () => { + // prevents `Logger not set.` and other appContext errors + mockContract = createAppContextStartContractMock(); + appContextService.start(mockContract); + }); + + describe('getPolicySecretPaths', () => { + describe('integration package with one policy template', () => { + const mockIntegrationPackage = { + name: 'mock-package', + title: 'Mock package', + version: '0[0].0', + description: 'description', + type: 'integration', + status: 'not_installed', + vars: [ + { name: 'pkg-secret-1', type: 'text', secret: true }, + { name: 'pkg-secret-2', type: 'text', secret: true }, + ], + data_streams: [ + { + dataset: 'somedataset', + streams: [ + { + input: 'foo', + title: 'Foo', + vars: [ + { name: 'stream-secret-1', type: 'text', secret: true }, + { name: 'stream-secret-2', type: 'text', secret: true }, + ], + }, + ], + }, + ], + policy_templates: [ + { + name: 'pkgPolicy1', + title: 'Package policy 1', + description: 'test package policy', + inputs: [ + { + type: 'foo', + title: 'Foo', + vars: [ + { default: 'foo-input-var-value', name: 'foo-input-var-name', type: 'text' }, + { + name: 'input-secret-1', + type: 'text', + secret: true, + }, + { + name: 'input-secret-2', + type: 'text', + secret: true, + }, + { name: 'foo-input3-var-name', type: 'text', multi: true }, + ], + }, + ], + }, + ], + } as unknown as PackageInfo; + it('policy with package level secret vars', () => { + const packagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, + 'pkg-secret-2': { + value: 'pkg-secret-2-val', + }, + }, + inputs: [], + } as unknown as NewPackagePolicy; + + expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ + { + path: 'vars.pkg-secret-1', + value: { + value: 'pkg-secret-1-val', + }, + }, + { + path: 'vars.pkg-secret-2', + value: { + value: 'pkg-secret-2-val', + }, + }, + ]); + }); + it('policy with package level secret vars and only one set', () => { + const packagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, + }, + inputs: [], + } as unknown as NewPackagePolicy; + + expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ + { + path: 'vars.pkg-secret-1', + value: { + value: 'pkg-secret-1-val', + }, + }, + ]); + }); + it('policy with input level secret vars', () => { + const packagePolicy = { + inputs: [ { - input: 'foo', - title: 'Foo', - vars: [ - { name: 'stream-secret-1', type: 'text', secret: true }, - { name: 'stream-secret-2', type: 'text', secret: true }, - ], + type: 'foo', + policy_template: 'pkgPolicy1', + vars: { + 'input-secret-1': { + value: 'input-secret-1-val', + }, + 'input-secret-2': { + value: 'input-secret-2-val', + }, + }, + streams: [], }, ], - }, - ], - policy_templates: [ - { - name: 'pkgPolicy1', - title: 'Package policy 1', - description: 'test package policy', + } as unknown as NewPackagePolicy; + + expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ + { + path: 'inputs[0].vars.input-secret-1', + value: { value: 'input-secret-1-val' }, + }, + { + path: 'inputs[0].vars.input-secret-2', + value: { value: 'input-secret-2-val' }, + }, + ]); + }); + it('stream level secret vars', () => { + const packagePolicy = { inputs: [ { type: 'foo', - title: 'Foo', - vars: [ - { default: 'foo-input-var-value', name: 'foo-input-var-name', type: 'text' }, - { - name: 'input-secret-1', - type: 'text', - secret: true, - }, + policy_template: 'pkgPolicy1', + streams: [ { - name: 'input-secret-2', - type: 'text', - secret: true, + data_stream: { + dataset: 'somedataset', + type: 'logs', + }, + vars: { + 'stream-secret-1': { + value: 'stream-secret-1-value', + }, + 'stream-secret-2': { + value: 'stream-secret-2-value', + }, + }, }, - { name: 'foo-input3-var-name', type: 'text', multi: true }, ], }, ], - }, - ], - } as unknown as PackageInfo; - it('policy with package level secret vars', () => { - const packagePolicy = { - vars: { - 'pkg-secret-1': { - value: 'pkg-secret-1-val', - }, - 'pkg-secret-2': { - value: 'pkg-secret-2-val', - }, - }, - inputs: [], - } as unknown as NewPackagePolicy; + } as unknown as NewPackagePolicy; - expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ - { - path: 'vars.pkg-secret-1', - value: { - value: 'pkg-secret-1-val', + expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ + { + path: 'inputs[0].streams[0].vars.stream-secret-1', + value: { value: 'stream-secret-1-value' }, }, - }, - { - path: 'vars.pkg-secret-2', - value: { - value: 'pkg-secret-2-val', + { + path: 'inputs[0].streams[0].vars.stream-secret-2', + value: { value: 'stream-secret-2-value' }, }, - }, - ]); + ]); + }); }); - it('policy with package level secret vars and only one set', () => { - const packagePolicy = { - vars: { - 'pkg-secret-1': { - value: 'pkg-secret-1-val', - }, - }, - inputs: [], - } as unknown as NewPackagePolicy; - expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ - { - path: 'vars.pkg-secret-1', - value: { - value: 'pkg-secret-1-val', + describe('integration package with multiple policy templates (e.g AWS)', () => { + const miniAWsPackage = { + name: 'aws', + title: 'AWS', + version: '0.5.3', + release: 'beta', + description: 'AWS Integration', + type: 'integration', + policy_templates: [ + { + name: 'billing', + title: 'AWS Billing', + description: 'Collect AWS billing metrics', + data_streams: ['billing'], + inputs: [ + { + type: 'aws/metrics', + title: 'Collect billing metrics', + description: 'Collect billing metrics', + input_group: 'metrics', + vars: [ + { + name: 'password', + type: 'text', + secret: true, + }, + ], + }, + ], }, - }, - ]); - }); - it('policy with input level secret vars', () => { - const packagePolicy = { - inputs: [ - { - type: 'foo', - policy_template: 'pkgPolicy1', - vars: { - 'input-secret-1': { - value: 'input-secret-1-val', + { + name: 'cloudtrail', + title: 'AWS Cloudtrail', + description: 'Collect logs from AWS Cloudtrail', + data_streams: ['cloudtrail'], + inputs: [ + { + type: 's3', + title: 'Collect logs from Cloudtrail service', + description: 'Collecting Cloudtrail logs using S3 input', + input_group: 'logs', + vars: [ + { + name: 'password', + type: 'text', + secret: true, + }, + ], }, - 'input-secret-2': { - value: 'input-secret-2-val', + { + type: 'httpjson', + title: 'Collect logs from third-party REST API (experimental)', + description: 'Collect logs from third-party REST API (experimental)', + input_group: 'logs', + vars: [ + { + name: 'password', + type: 'text', + secret: true, + }, + ], }, - }, - streams: [], + ], }, ], - } as unknown as NewPackagePolicy; - - expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ - { - path: 'inputs[0].vars.input-secret-1', - value: { value: 'input-secret-1-val' }, - }, - { - path: 'inputs[0].vars.input-secret-2', - value: { value: 'input-secret-2-val' }, - }, - ]); - }); - it('stream level secret vars', () => { - const packagePolicy = { - inputs: [ + vars: [ { - type: 'foo', - policy_template: 'pkgPolicy1', + name: 'secret_access_key', + type: 'text', + title: 'Secret Access Key', + multi: false, + required: false, + show_user: false, + secret: true, + }, + ], + data_streams: [ + { + type: 'metrics', + dataset: 'aws.billing', + title: 'AWS billing metrics', + release: 'beta', streams: [ { - data_stream: { - dataset: 'somedataset', - type: 'logs', - }, - vars: { - 'stream-secret-1': { - value: 'stream-secret-1-value', + input: 'aws/metrics', + vars: [ + { + name: 'password', + type: 'text', + secret: true, }, - 'stream-secret-2': { - value: 'stream-secret-2-value', + ], + template_path: 'stream.yml.hbs', + title: 'AWS Billing metrics', + description: 'Collect AWS billing metrics', + enabled: true, + }, + ], + package: 'aws', + path: 'billing', + }, + { + type: 'logs', + dataset: 'aws.cloudtrail', + title: 'AWS CloudTrail logs', + release: 'beta', + ingest_pipeline: 'default', + streams: [ + { + input: 's3', + vars: [ + { + name: 'password', + type: 'text', + secret: true, }, - }, + ], + template_path: 's3.yml.hbs', + }, + { + input: 'httpjson', + vars: [ + { + name: 'username', + type: 'text', + title: 'Splunk REST API Username', + multi: false, + required: true, + show_user: true, + }, + { + name: 'password', + type: 'password', + title: 'Splunk REST API Password', + multi: false, + required: true, + show_user: true, + secret: true, + }, + ], + template_path: 'httpjson.yml.hbs', }, ], + package: 'aws', + path: 'cloudtrail', }, ], - } as unknown as NewPackagePolicy; - - expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ - { - path: 'inputs[0].streams[0].vars.stream-secret-1', - value: { value: 'stream-secret-1-value' }, - }, - { - path: 'inputs[0].streams[0].vars.stream-secret-2', - value: { value: 'stream-secret-2-value' }, - }, - ]); - }); - }); - - describe('integration package with multiple policy templates (e.g AWS)', () => { - const miniAWsPackage = { - name: 'aws', - title: 'AWS', - version: '0.5.3', - release: 'beta', - description: 'AWS Integration', - type: 'integration', - policy_templates: [ - { - name: 'billing', - title: 'AWS Billing', - description: 'Collect AWS billing metrics', - data_streams: ['billing'], + } as PackageInfo; + it('single policy with package + input + stream level secret var', () => { + const policy = { + vars: { + secret_access_key: { + value: 'my_secret_access_key', + }, + }, inputs: [ { type: 'aws/metrics', - title: 'Collect billing metrics', - description: 'Collect billing metrics', - input_group: 'metrics', - vars: [ + policy_template: 'billing', + enabled: true, + vars: { + password: { value: 'billing_input_password', type: 'text' }, + }, + streams: [ { - name: 'password', - type: 'text', - secret: true, + enabled: true, + data_stream: { type: 'metrics', dataset: 'aws.billing' }, + vars: { + password: { value: 'billing_stream_password', type: 'text' }, + }, }, ], }, ], - }, - { - name: 'cloudtrail', - title: 'AWS Cloudtrail', - description: 'Collect logs from AWS Cloudtrail', - data_streams: ['cloudtrail'], - inputs: [ - { - type: 's3', - title: 'Collect logs from Cloudtrail service', - description: 'Collecting Cloudtrail logs using S3 input', - input_group: 'logs', - vars: [ - { - name: 'password', - type: 'text', - secret: true, - }, - ], + }; + expect( + getPolicySecretPaths( + policy as unknown as NewPackagePolicy, + miniAWsPackage as unknown as PackageInfo + ) + ).toEqual([ + { + path: 'vars.secret_access_key', + value: { + value: 'my_secret_access_key', + }, + }, + { + path: 'inputs[0].vars.password', + value: { + type: 'text', + value: 'billing_input_password', + }, + }, + { + path: 'inputs[0].streams[0].vars.password', + value: { + type: 'text', + value: 'billing_stream_password', + }, + }, + ]); + }); + it('double policy with package + input + stream level secret var', () => { + const policy = { + vars: { + secret_access_key: { + value: 'my_secret_access_key', }, + }, + inputs: [ { type: 'httpjson', - title: 'Collect logs from third-party REST API (experimental)', - description: 'Collect logs from third-party REST API (experimental)', - input_group: 'logs', - vars: [ + policy_template: 'cloudtrail', + enabled: false, + vars: { + password: { value: 'cloudtrail_httpjson_input_password' }, + }, + streams: [ { - name: 'password', - type: 'text', - secret: true, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + username: { value: 'hop_dev' }, + password: { value: 'cloudtrail_httpjson_stream_password' }, + }, }, ], }, - ], - }, - ], - vars: [ - { - name: 'secret_access_key', - type: 'text', - title: 'Secret Access Key', - multi: false, - required: false, - show_user: false, - secret: true, - }, - ], - data_streams: [ - { - type: 'metrics', - dataset: 'aws.billing', - title: 'AWS billing metrics', - release: 'beta', - streams: [ { - input: 'aws/metrics', - vars: [ + type: 's3', + policy_template: 'cloudtrail', + enabled: true, + vars: { + password: { value: 'cloudtrail_s3_input_password' }, + }, + streams: [ { - name: 'password', - type: 'text', - secret: true, + enabled: true, + data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, + vars: { + password: { value: 'cloudtrail_s3_stream_password' }, + }, }, ], - template_path: 'stream.yml.hbs', - title: 'AWS Billing metrics', - description: 'Collect AWS billing metrics', - enabled: true, }, ], - package: 'aws', - path: 'billing', - }, - { - type: 'logs', - dataset: 'aws.cloudtrail', - title: 'AWS CloudTrail logs', - release: 'beta', - ingest_pipeline: 'default', - streams: [ - { - input: 's3', - vars: [ - { - name: 'password', - type: 'text', - secret: true, - }, - ], - template_path: 's3.yml.hbs', + }; + + expect( + getPolicySecretPaths( + policy as unknown as NewPackagePolicy, + miniAWsPackage as unknown as PackageInfo + ) + ).toEqual([ + { + path: 'vars.secret_access_key', + value: { + value: 'my_secret_access_key', }, - { - input: 'httpjson', - vars: [ - { - name: 'username', - type: 'text', - title: 'Splunk REST API Username', - multi: false, - required: true, - show_user: true, - }, - { - name: 'password', - type: 'password', - title: 'Splunk REST API Password', - multi: false, - required: true, - show_user: true, - secret: true, - }, - ], - template_path: 'httpjson.yml.hbs', + }, + { + path: 'inputs[0].vars.password', + value: { + value: 'cloudtrail_httpjson_input_password', }, - ], - package: 'aws', - path: 'cloudtrail', - }, - ], - } as PackageInfo; - it('single policy with package + input + stream level secret var', () => { - const policy = { - vars: { - secret_access_key: { - value: 'my_secret_access_key', }, - }, - inputs: [ { - type: 'aws/metrics', - policy_template: 'billing', - enabled: true, - vars: { - password: { value: 'billing_input_password', type: 'text' }, + path: 'inputs[0].streams[0].vars.password', + value: { + value: 'cloudtrail_httpjson_stream_password', }, - streams: [ + }, + { + path: 'inputs[1].vars.password', + value: { + value: 'cloudtrail_s3_input_password', + }, + }, + { + path: 'inputs[1].streams[0].vars.password', + value: { + value: 'cloudtrail_s3_stream_password', + }, + }, + ]); + }); + }); + + describe('input package', () => { + const mockInputPackage = { + name: 'log', + version: '2.0.0', + description: 'Collect custom logs with Elastic Agent.', + title: 'Custom Logs', + format_version: '2.6.0', + owner: { + github: 'elastic/elastic-agent-data-plane', + }, + type: 'input', + categories: ['custom', 'custom_logs'], + conditions: {}, + icons: [], + policy_templates: [ + { + name: 'logs', + title: 'Custom log file', + description: 'Collect your custom log files.', + multiple: true, + input: 'logfile', + type: 'logs', + template_path: 'input.yml.hbs', + vars: [ { - enabled: true, - data_stream: { type: 'metrics', dataset: 'aws.billing' }, - vars: { - password: { value: 'billing_stream_password', type: 'text' }, - }, + name: 'paths', + required: true, + title: 'Log file path', + description: 'Path to log files to be collected', + type: 'text', + multi: true, + }, + { + name: 'data_stream.dataset', + required: true, + title: 'Dataset name', + description: + "Set the name for your dataset. Changing the dataset will send the data to a different index. You can't use `-` in the name of a dataset and only valid characters for [Elasticsearch index names](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html).\n", + type: 'text', + }, + { + name: 'secret-1', + type: 'text', + secret: true, + }, + { + name: 'secret-2', + type: 'text', + secret: true, }, ], }, ], }; - expect( - getPolicySecretPaths( - policy as unknown as NewPackagePolicy, - miniAWsPackage as unknown as PackageInfo - ) - ).toEqual([ + it('template level vars', () => { + const policy = { + inputs: [ + { + type: 'logfile', + policy_template: 'logs', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'logs', + dataset: 'log.logs', + }, + vars: { + paths: { + value: ['/tmp/test.log'], + }, + 'data_stream.dataset': { + value: 'hello', + }, + 'secret-1': { + value: 'secret-1-value', + }, + 'secret-2': { + value: 'secret-2-value', + }, + }, + }, + ], + }, + ], + }; + + expect( + getPolicySecretPaths( + policy as unknown as NewPackagePolicy, + mockInputPackage as unknown as PackageInfo + ) + ).toEqual([ + { + path: 'inputs[0].streams[0].vars.secret-1', + value: { + value: 'secret-1-value', + }, + }, + { + path: 'inputs[0].streams[0].vars.secret-2', + value: { + value: 'secret-2-value', + }, + }, + ]); + }); + }); + }); + + describe('diffSecretPaths', () => { + it('should return empty array if no secrets', () => { + expect(diffSecretPaths([], [])).toEqual({ + toCreate: [], + toDelete: [], + noChange: [], + }); + }); + it('should return empty array if single secret not changed', () => { + const paths = [ { - path: 'vars.secret_access_key', + path: 'somepath', value: { - value: 'my_secret_access_key', + value: { + isSecretRef: true, + id: 'secret-1', + }, }, }, + ]; + expect(diffSecretPaths(paths, paths)).toEqual({ + toCreate: [], + toDelete: [], + noChange: paths, + }); + }); + it('should return empty array if multiple secrets not changed', () => { + const paths = [ { - path: 'inputs[0].vars.password', + path: 'somepath', value: { - type: 'text', - value: 'billing_input_password', + value: { + isSecretRef: true, + id: 'secret-1', + }, }, }, { - path: 'inputs[0].streams[0].vars.password', + path: 'somepath2', value: { - type: 'text', - value: 'billing_stream_password', + value: { + isSecretRef: true, + id: 'secret-2', + }, }, }, - ]); + { + path: 'somepath3', + value: { + value: { + isSecretRef: true, + id: 'secret-3', + }, + }, + }, + ]; + + expect(diffSecretPaths(paths, paths.slice().reverse())).toEqual({ + toCreate: [], + toDelete: [], + noChange: paths, + }); }); - it('double policy with package + input + stream level secret var', () => { - const policy = { - vars: { - secret_access_key: { - value: 'my_secret_access_key', + it('single secret modified', () => { + const paths1 = [ + { + path: 'somepath1', + value: { + value: { + isSecretRef: true, + id: 'secret-1', + }, + }, + }, + { + path: 'somepath2', + value: { + value: { isSecretRef: true, id: 'secret-2' }, }, }, - inputs: [ + ]; + + const paths2 = [ + paths1[0], + { + path: 'somepath2', + value: { value: 'newvalue' }, + }, + ]; + + expect(diffSecretPaths(paths1, paths2)).toEqual({ + toCreate: [ { - type: 'httpjson', - policy_template: 'cloudtrail', - enabled: false, - vars: { - password: { value: 'cloudtrail_httpjson_input_password' }, - }, - streams: [ - { - data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, - vars: { - username: { value: 'hop_dev' }, - password: { value: 'cloudtrail_httpjson_stream_password' }, - }, - }, - ], + path: 'somepath2', + value: { value: 'newvalue' }, }, + ], + toDelete: [ { - type: 's3', - policy_template: 'cloudtrail', - enabled: true, - vars: { - password: { value: 'cloudtrail_s3_input_password' }, - }, - streams: [ - { - enabled: true, - data_stream: { type: 'logs', dataset: 'aws.cloudtrail' }, - vars: { - password: { value: 'cloudtrail_s3_stream_password' }, - }, + path: 'somepath2', + value: { + value: { + isSecretRef: true, + id: 'secret-2', }, - ], + }, }, ], - }; - - expect( - getPolicySecretPaths( - policy as unknown as NewPackagePolicy, - miniAWsPackage as unknown as PackageInfo - ) - ).toEqual([ + noChange: [paths1[0]], + }); + }); + it('double secret modified', () => { + const paths1 = [ { - path: 'vars.secret_access_key', + path: 'somepath1', value: { - value: 'my_secret_access_key', + value: { + isSecretRef: true, + id: 'secret-1', + }, }, }, { - path: 'inputs[0].vars.password', + path: 'somepath2', value: { - value: 'cloudtrail_httpjson_input_password', + value: { + isSecretRef: true, + id: 'secret-2', + }, }, }, + ]; + + const paths2 = [ { - path: 'inputs[0].streams[0].vars.password', - value: { - value: 'cloudtrail_httpjson_stream_password', - }, + path: 'somepath1', + value: { value: 'newvalue1' }, }, { - path: 'inputs[1].vars.password', - value: { - value: 'cloudtrail_s3_input_password', - }, + path: 'somepath2', + value: { value: 'newvalue2' }, }, + ]; + + expect(diffSecretPaths(paths1, paths2)).toEqual({ + toCreate: [ + { + path: 'somepath1', + value: { value: 'newvalue1' }, + }, + { + path: 'somepath2', + value: { value: 'newvalue2' }, + }, + ], + toDelete: [ + { + path: 'somepath1', + value: { + value: { + isSecretRef: true, + id: 'secret-1', + }, + }, + }, + { + path: 'somepath2', + value: { + value: { + isSecretRef: true, + id: 'secret-2', + }, + }, + }, + ], + noChange: [], + }); + }); + + it('single secret added', () => { + const paths1 = [ { - path: 'inputs[1].streams[0].vars.password', + path: 'somepath1', value: { - value: 'cloudtrail_s3_stream_password', + value: { + isSecretRef: true, + id: 'secret-1', + }, }, }, - ]); + ]; + + const paths2 = [ + paths1[0], + { + path: 'somepath2', + value: { value: 'newvalue' }, + }, + ]; + + expect(diffSecretPaths(paths1, paths2)).toEqual({ + toCreate: [ + { + path: 'somepath2', + value: { value: 'newvalue' }, + }, + ], + toDelete: [], + noChange: [paths1[0]], + }); }); }); - describe('input package', () => { - const mockInputPackage = { - name: 'log', - version: '2.0.0', - description: 'Collect custom logs with Elastic Agent.', - title: 'Custom Logs', - format_version: '2.6.0', - owner: { - github: 'elastic/elastic-agent-data-plane', - }, - type: 'input', - categories: ['custom', 'custom_logs'], - conditions: {}, - icons: [], - policy_templates: [ + describe('extractAndWriteSecrets', () => { + const esClientMock = elasticsearchServiceMock.createInternalClient(); + + esClientMock.transport.request.mockImplementation(async (req) => { + return { + id: uuidv4(), + }; + }); + + beforeEach(() => { + esClientMock.transport.request.mockClear(); + }); + + const mockIntegrationPackage = { + name: 'mock-package', + title: 'Mock package', + version: '0.0.0', + description: 'description', + type: 'integration', + status: 'not_installed', + vars: [ + { name: 'pkg-secret-1', type: 'text', secret: true, required: true }, + { name: 'pkg-secret-2', type: 'text', secret: true, required: false }, + ], + data_streams: [ { - name: 'logs', - title: 'Custom log file', - description: 'Collect your custom log files.', - multiple: true, - input: 'logfile', - type: 'logs', - template_path: 'input.yml.hbs', - vars: [ - { - name: 'paths', - required: true, - title: 'Log file path', - description: 'Path to log files to be collected', - type: 'text', - multi: true, - }, - { - name: 'data_stream.dataset', - required: true, - title: 'Dataset name', - description: - "Set the name for your dataset. Changing the dataset will send the data to a different index. You can't use `-` in the name of a dataset and only valid characters for [Elasticsearch index names](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html).\n", - type: 'text', - }, + dataset: 'somedataset', + streams: [ { - name: 'secret-1', - type: 'text', - secret: true, + input: 'foo', + title: 'Foo', }, + ], + }, + ], + policy_templates: [ + { + name: 'pkgPolicy1', + title: 'Package policy 1', + description: 'test package policy', + inputs: [ { - name: 'secret-2', - type: 'text', - secret: true, + type: 'foo', + title: 'Foo', + vars: [], }, ], }, ], - }; - it('template level vars', () => { - const policy = { - inputs: [ - { - type: 'logfile', - policy_template: 'logs', - enabled: true, - streams: [ - { - enabled: true, - data_stream: { - type: 'logs', - dataset: 'log.logs', - }, - vars: { - paths: { - value: ['/tmp/test.log'], - }, - 'data_stream.dataset': { - value: 'hello', - }, - 'secret-1': { - value: 'secret-1-value', - }, - 'secret-2': { - value: 'secret-2-value', - }, - }, - }, - ], - }, - ], - }; + } as unknown as PackageInfo; - expect( - getPolicySecretPaths( - policy as unknown as NewPackagePolicy, - mockInputPackage as unknown as PackageInfo - ) - ).toEqual([ - { - path: 'inputs[0].streams[0].vars.secret-1', - value: { - value: 'secret-1-value', + describe('when only required secret value is provided', () => { + it('returns single secret reference for required secret', async () => { + const mockPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, }, - }, - { - path: 'inputs[0].streams[0].vars.secret-2', - value: { - value: 'secret-2-value', + inputs: [], + } as unknown as NewPackagePolicy; + + const result = await extractAndWriteSecrets({ + packagePolicy: mockPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(1); + expect(result.secretReferences).toHaveLength(1); + }); + }); + + describe('when both required and optional secret values are provided', () => { + it('returns secret reference for both required and optional secret', async () => { + const mockPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, + 'pkg-secret-2': { + value: 'pkg-secret-2-val', + }, }, - }, - ]); + inputs: [], + } as unknown as NewPackagePolicy; + + const result = await extractAndWriteSecrets({ + packagePolicy: mockPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(2); + expect(result.secretReferences).toHaveLength(2); + }); }); }); }); -describe('diffSecretPaths', () => { +describe('diffOutputSecretPaths', () => { it('should return empty array if no secrets', () => { - expect(diffSecretPaths([], [])).toEqual({ + expect(diffOutputSecretPaths([], [])).toEqual({ toCreate: [], toDelete: [], noChange: [], @@ -600,14 +934,11 @@ describe('diffSecretPaths', () => { { path: 'somepath', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, ]; - expect(diffSecretPaths(paths, paths)).toEqual({ + expect(diffOutputSecretPaths(paths, paths)).toEqual({ toCreate: [], toDelete: [], noChange: paths, @@ -618,33 +949,24 @@ describe('diffSecretPaths', () => { { path: 'somepath', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, { path: 'somepath2', value: { - value: { - isSecretRef: true, - id: 'secret-2', - }, + id: 'secret-2', }, }, { path: 'somepath3', value: { - value: { - isSecretRef: true, - id: 'secret-3', - }, + id: 'secret-3', }, }, ]; - expect(diffSecretPaths(paths, paths.slice().reverse())).toEqual({ + expect(diffOutputSecretPaths(paths, paths.slice().reverse())).toEqual({ toCreate: [], toDelete: [], noChange: paths, @@ -655,16 +977,13 @@ describe('diffSecretPaths', () => { { path: 'somepath1', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, { path: 'somepath2', value: { - value: { isSecretRef: true, id: 'secret-2' }, + id: 'secret-2', }, }, ]; @@ -673,25 +992,22 @@ describe('diffSecretPaths', () => { paths1[0], { path: 'somepath2', - value: { value: 'newvalue' }, + value: 'newvalue', }, ]; - expect(diffSecretPaths(paths1, paths2)).toEqual({ + expect(diffOutputSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { path: 'somepath2', - value: { value: 'newvalue' }, + value: 'newvalue', }, ], toDelete: [ { path: 'somepath2', value: { - value: { - isSecretRef: true, - id: 'secret-2', - }, + id: 'secret-2', }, }, ], @@ -703,19 +1019,13 @@ describe('diffSecretPaths', () => { { path: 'somepath1', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, { path: 'somepath2', value: { - value: { - isSecretRef: true, - id: 'secret-2', - }, + id: 'secret-2', }, }, ]; @@ -723,42 +1033,36 @@ describe('diffSecretPaths', () => { const paths2 = [ { path: 'somepath1', - value: { value: 'newvalue1' }, + value: 'newvalue1', }, { path: 'somepath2', - value: { value: 'newvalue2' }, + value: 'newvalue2', }, ]; - expect(diffSecretPaths(paths1, paths2)).toEqual({ + expect(diffOutputSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { path: 'somepath1', - value: { value: 'newvalue1' }, + value: 'newvalue1', }, { path: 'somepath2', - value: { value: 'newvalue2' }, + value: 'newvalue2', }, ], toDelete: [ { path: 'somepath1', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, { path: 'somepath2', value: { - value: { - isSecretRef: true, - id: 'secret-2', - }, + id: 'secret-2', }, }, ], @@ -771,10 +1075,7 @@ describe('diffSecretPaths', () => { { path: 'somepath1', value: { - value: { - isSecretRef: true, - id: 'secret-1', - }, + id: 'secret-1', }, }, ]; @@ -783,15 +1084,15 @@ describe('diffSecretPaths', () => { paths1[0], { path: 'somepath2', - value: { value: 'newvalue' }, + value: 'newvalue', }, ]; - expect(diffSecretPaths(paths1, paths2)).toEqual({ + expect(diffOutputSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { path: 'somepath2', - value: { value: 'newvalue' }, + value: 'newvalue', }, ], toDelete: [], diff --git a/x-pack/plugins/fleet/server/services/secrets.ts b/x-pack/plugins/fleet/server/services/secrets.ts index 36a88b4a7a4c1..c425a8a9e172a 100644 --- a/x-pack/plugins/fleet/server/services/secrets.ts +++ b/x-pack/plugins/fleet/server/services/secrets.ts @@ -7,10 +7,16 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import { keyBy } from 'lodash'; +import { get, keyBy } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; -import type { KafkaOutput, Output, OutputSecretPath } from '../../common/types'; +import type { + KafkaOutput, + NewLogstashOutput, + NewRemoteElasticsearchOutput, + Output, + OutputSecretPath, +} from '../../common/types'; import { packageHasNoPolicyTemplates } from '../../common/services/policy_template'; @@ -228,13 +234,15 @@ export async function extractAndWriteSecrets(opts: { return { packagePolicy, secretReferences: [] }; } + const secretsToCreate = secretPaths.filter((secretPath) => !!secretPath.value.value); + const secrets = await createSecrets({ esClient, - values: secretPaths.map((secretPath) => secretPath.value.value), + values: secretsToCreate.map((secretPath) => secretPath.value.value), }); const policyWithSecretRefs = JSON.parse(JSON.stringify(packagePolicy)); - secretPaths.forEach((secretPath, i) => { + secretsToCreate.forEach((secretPath, i) => { set(policyWithSecretRefs, secretPath.path + '.value', toVarSecretRef(secrets[i].id)); }); @@ -247,8 +255,9 @@ export async function extractAndWriteSecrets(opts: { export async function extractAndWriteOutputSecrets(opts: { output: NewOutput; esClient: ElasticsearchClient; + secretHashes?: Record; }): Promise<{ output: NewOutput; secretReferences: PolicySecretReference[] }> { - const { output, esClient } = opts; + const { output, esClient, secretHashes = {} } = opts; const secretPaths = getOutputSecretPaths(output.type, output).filter( (path) => typeof path.value === 'string' @@ -265,7 +274,12 @@ export async function extractAndWriteOutputSecrets(opts: { const outputWithSecretRefs = JSON.parse(JSON.stringify(output)); secretPaths.forEach((secretPath, i) => { - set(outputWithSecretRefs, secretPath.path, { id: secrets[i].id }); + const pathWithoutPrefix = secretPath.path.replace('secrets.', ''); + const maybeHash = get(secretHashes, pathWithoutPrefix); + set(outputWithSecretRefs, secretPath.path, { + id: secrets[i].id, + ...(typeof maybeHash === 'string' && { hash: maybeHash }), + }); }); return { @@ -280,11 +294,14 @@ function getOutputSecretPaths( ): OutputSecretPath[] { const outputSecretPaths: OutputSecretPath[] = []; - if ((outputType === 'kafka' || outputType === 'logstash') && output.secrets?.ssl?.key) { - outputSecretPaths.push({ - path: 'secrets.ssl.key', - value: output.secrets.ssl.key, - }); + if (outputType === 'logstash') { + const logstashOutput = output as NewLogstashOutput; + if (logstashOutput?.secrets?.ssl?.key) { + outputSecretPaths.push({ + path: 'secrets.ssl.key', + value: logstashOutput.secrets.ssl.key, + }); + } } if (outputType === 'kafka') { @@ -295,6 +312,22 @@ function getOutputSecretPaths( value: kafkaOutput.secrets.password, }); } + if (kafkaOutput?.secrets?.ssl?.key) { + outputSecretPaths.push({ + path: 'secrets.ssl.key', + value: kafkaOutput.secrets.ssl.key, + }); + } + } + + if (outputType === 'remote_elasticsearch') { + const remoteESOutput = output as NewRemoteElasticsearchOutput; + if (remoteESOutput.secrets?.service_token) { + outputSecretPaths.push({ + path: 'secrets.service_token', + value: remoteESOutput.secrets.service_token, + }); + } } return outputSecretPaths; @@ -340,6 +373,15 @@ export function getOutputSecretReferences(output: Output): PolicySecretReference }); } + if ( + output.type === 'remote_elasticsearch' && + typeof output?.secrets?.service_token === 'object' + ) { + outputSecretPaths.push({ + id: output.secrets.service_token.id, + }); + } + return outputSecretPaths; } @@ -384,7 +426,7 @@ export async function extractAndUpdateSecrets(opts: { // check if the previous secret is actually a secret refrerence // it may be that secrets were not enabled at the time of creation // in which case they are just stored as plain text - if (secretPath.value.value.isSecretRef) { + if (secretPath.value.value?.isSecretRef) { secretsToDelete.push({ id: secretPath.value.value.id }); } }); @@ -399,12 +441,13 @@ export async function extractAndUpdateOutputSecrets(opts: { oldOutput: Output; outputUpdate: Partial; esClient: ElasticsearchClient; + secretHashes?: Record; }): Promise<{ outputUpdate: Partial; secretReferences: PolicySecretReference[]; secretsToDelete: PolicySecretReference[]; }> { - const { oldOutput, outputUpdate, esClient } = opts; + const { oldOutput, outputUpdate, esClient, secretHashes } = opts; const outputType = outputUpdate.type || oldOutput.type; const oldSecretPaths = getOutputSecretPaths(outputType, oldOutput); const updatedSecretPaths = getOutputSecretPaths(outputType, outputUpdate); @@ -425,7 +468,13 @@ export async function extractAndUpdateOutputSecrets(opts: { const outputWithSecretRefs = JSON.parse(JSON.stringify(outputUpdate)); toCreate.forEach((secretPath, i) => { - set(outputWithSecretRefs, secretPath.path, { id: createdSecrets[i].id }); + const pathWithoutPrefix = secretPath.path.replace('secrets.', ''); + const maybeHash = get(secretHashes, pathWithoutPrefix); + + set(outputWithSecretRefs, secretPath.path, { + id: createdSecrets[i].id, + ...(typeof maybeHash === 'string' && { hash: maybeHash }), + }); }); const secretReferences = [ @@ -509,10 +558,12 @@ export function diffOutputSecretPaths( const newPath = newPathsByPath[oldPath.path]; if (newPath && newPath.value) { const newValue = newPath.value; - if (typeof newValue === 'string') toCreate.push(newPath); - toDelete.push(oldPath); - } else { - noChange.push(newPath); + if (typeof newValue === 'string') { + toCreate.push(newPath); + toDelete.push(oldPath); + } else { + noChange.push(newPath); + } } delete newPathsByPath[oldPath.path]; } diff --git a/x-pack/plugins/fleet/server/services/security/fleet_router.test.ts b/x-pack/plugins/fleet/server/services/security/fleet_router.test.ts index 1e7d09f261603..5de46b845bfcc 100644 --- a/x-pack/plugins/fleet/server/services/security/fleet_router.test.ts +++ b/x-pack/plugins/fleet/server/services/security/fleet_router.test.ts @@ -5,7 +5,11 @@ * 2.0. */ -import type { CheckPrivilegesDynamically } from '@kbn/security-plugin/server/authorization/check_privileges_dynamically'; +import type { + CheckPrivilegesDynamically, + CheckPrivilegesResponse, + CheckPrivilegesPayload, +} from '@kbn/security-plugin/server'; import type { RequestHandler } from '@kbn/core/server'; import type { VersionedRouter } from '@kbn/core-http-server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; @@ -14,10 +18,6 @@ import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { coreMock } from '@kbn/core/server/mocks'; -import type { CheckPrivilegesPayload } from '@kbn/security-plugin/server'; - -import type { CheckPrivilegesResponse } from '@kbn/security-plugin/server/authorization/types'; - import { API_VERSIONS } from '../../../common/constants'; import type { FleetRequestHandlerContext } from '../..'; diff --git a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.test.ts b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.test.ts index 4cf657b7255c5..4b3be1de81632 100644 --- a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.test.ts +++ b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.test.ts @@ -499,5 +499,80 @@ describe('UninstallTokenService', () => { }); }); }); + + describe('check validity of tokens', () => { + const okaySO = getDefaultSO(canEncrypt); + + const errorWithDecryptionSO2 = { + ...getDefaultSO2(canEncrypt), + error: new Error('error reason'), + }; + const missingTokenSO2 = { + ...getDefaultSO2(canEncrypt), + attributes: { + ...getDefaultSO2(canEncrypt).attributes, + token: undefined, + token_plain: undefined, + }, + }; + + describe('checkTokenValidityForAllPolicies', () => { + it('resolves if all of the tokens are available', async () => { + mockCreatePointInTimeFinderAsInternalUser(); + + await expect( + uninstallTokenService.checkTokenValidityForAllPolicies() + ).resolves.not.toThrowError(); + }); + + it('rejects if any of the tokens is missing', async () => { + mockCreatePointInTimeFinderAsInternalUser([okaySO, missingTokenSO2]); + + await expect( + uninstallTokenService.checkTokenValidityForAllPolicies() + ).rejects.toThrowError( + 'Invalid uninstall token: Saved object is missing the `token` attribute.' + ); + }); + + it('rejects if token decryption gives error', async () => { + mockCreatePointInTimeFinderAsInternalUser([okaySO, errorWithDecryptionSO2]); + + await expect( + uninstallTokenService.checkTokenValidityForAllPolicies() + ).rejects.toThrowError('Error when reading Uninstall Token: error reason'); + }); + }); + + describe('checkTokenValidityForPolicy', () => { + it('resolves if token is available', async () => { + mockCreatePointInTimeFinderAsInternalUser(); + + await expect( + uninstallTokenService.checkTokenValidityForPolicy(okaySO.attributes.policy_id) + ).resolves.not.toThrowError(); + }); + + it('rejects if token is missing', async () => { + mockCreatePointInTimeFinderAsInternalUser([okaySO, missingTokenSO2]); + + await expect( + uninstallTokenService.checkTokenValidityForPolicy(missingTokenSO2.attributes.policy_id) + ).rejects.toThrowError( + 'Invalid uninstall token: Saved object is missing the `token` attribute.' + ); + }); + + it('rejects if token decryption gives error', async () => { + mockCreatePointInTimeFinderAsInternalUser([okaySO, errorWithDecryptionSO2]); + + await expect( + uninstallTokenService.checkTokenValidityForPolicy( + errorWithDecryptionSO2.attributes.policy_id + ) + ).rejects.toThrowError('Error when reading Uninstall Token: error reason'); + }); + }); + }); }); }); diff --git a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts index 8309910be6f53..9e03e7869c584 100644 --- a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts +++ b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts @@ -109,7 +109,7 @@ export interface UninstallTokenServiceInterface { * @param force generate a new token even if one already exists * @returns hashedToken */ - generateTokenForPolicyId(policyId: string, force?: boolean): Promise; + generateTokenForPolicyId(policyId: string, force?: boolean): Promise; /** * Generate uninstall tokens for given policy ids @@ -119,7 +119,7 @@ export interface UninstallTokenServiceInterface { * @param force generate a new token even if one already exists * @returns Record */ - generateTokensForPolicyIds(policyIds: string[], force?: boolean): Promise>; + generateTokensForPolicyIds(policyIds: string[], force?: boolean): Promise; /** * Generate uninstall tokens all policies @@ -128,12 +128,26 @@ export interface UninstallTokenServiceInterface { * @param force generate a new token even if one already exists * @returns Record */ - generateTokensForAllPolicies(force?: boolean): Promise>; + generateTokensForAllPolicies(force?: boolean): Promise; /** * If encryption is available, checks for any plain text uninstall tokens and encrypts them */ encryptTokens(): Promise; + + /** + * Check whether the selected policy has a valid uninstall token. Rejects returning promise if not. + * + * @param policyId policy Id to check + */ + checkTokenValidityForPolicy(policyId: string): Promise; + + /** + * Check whether all policies have a valid uninstall token. Rejects returning promise if not. + * + * @param policyId policy Id to check + */ + checkTokenValidityForAllPolicies(): Promise; } export class UninstallTokenService implements UninstallTokenServiceInterface { @@ -210,7 +224,11 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { tokensFinder.close(); const uninstallTokens: UninstallToken[] = tokenObject.map( - ({ id: _id, attributes, created_at: createdAt }) => { + ({ id: _id, attributes, created_at: createdAt, error }) => { + if (error) { + throw new UninstallTokenError(`Error when reading Uninstall Token: ${error.message}`); + } + this.assertPolicyId(attributes); this.assertToken(attributes); this.assertCreatedAt(createdAt); @@ -304,32 +322,30 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { return this.getHashedTokensForPolicyIds(policyIds); } - public async generateTokenForPolicyId(policyId: string, force: boolean = false): Promise { - return (await this.generateTokensForPolicyIds([policyId], force))[policyId]; + public generateTokenForPolicyId(policyId: string, force: boolean = false): Promise { + return this.generateTokensForPolicyIds([policyId], force); } public async generateTokensForPolicyIds( policyIds: string[], force: boolean = false - ): Promise> { + ): Promise { const { agentTamperProtectionEnabled } = appContextService.getExperimentalFeatures(); if (!agentTamperProtectionEnabled || !policyIds.length) { - return {}; + return; } - const existingTokens = force - ? {} - : (await this.getDecryptedTokensForPolicyIds(policyIds)).reduce( - (acc, { policy_id: policyId, token }) => { - acc[policyId] = token; - return acc; - }, - {} as Record - ); + const existingTokens = new Set(); + + if (!force) { + (await this.getTokenObjectsByIncludeFilter(policyIds)).forEach((tokenObject) => { + existingTokens.add(tokenObject._source[UNINSTALL_TOKENS_SAVED_OBJECT_TYPE].policy_id); + }); + } const missingTokenPolicyIds = force ? policyIds - : policyIds.filter((policyId) => !existingTokens[policyId]); + : policyIds.filter((policyId) => !existingTokens.has(policyId)); const newTokensMap = missingTokenPolicyIds.reduce((acc, policyId) => { const token = this.generateToken(); @@ -338,7 +354,6 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { [policyId]: token, }; }, {} as Record); - await this.persistTokens(missingTokenPolicyIds, newTokensMap); if (force) { const config = appContextService.getConfig(); @@ -349,21 +364,9 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { await agentPolicyService.deployPolicies(this.soClient, policyIdsBatch) ); } - - const tokensMap = { - ...existingTokens, - ...newTokensMap, - }; - - return Object.entries(tokensMap).reduce((acc, [policyId, token]) => { - acc[policyId] = this.hashToken(token); - return acc; - }, {} as Record); } - public async generateTokensForAllPolicies( - force: boolean = false - ): Promise> { + public async generateTokensForAllPolicies(force: boolean = false): Promise { const policyIds = await this.getAllPolicyIds(); return this.generateTokensForPolicyIds(policyIds, force); } @@ -486,6 +489,15 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { return this._soClient; } + public async checkTokenValidityForPolicy(policyId: string): Promise { + await this.getDecryptedTokensForPolicyIds([policyId]); + } + + public async checkTokenValidityForAllPolicies(): Promise { + const policyIds = await this.getAllPolicyIds(); + await this.getDecryptedTokensForPolicyIds(policyIds); + } + private get isEncryptionAvailable(): boolean { return appContextService.getEncryptedSavedObjectsSetup()?.canEncrypt ?? false; } @@ -498,7 +510,9 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { private assertToken(attributes: UninstallTokenSOAttributes | undefined) { if (!attributes?.token && !attributes?.token_plain) { - throw new UninstallTokenError('Uninstall Token is missing the token.'); + throw new UninstallTokenError( + 'Invalid uninstall token: Saved object is missing the `token` attribute.' + ); } } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 270cfefb56a82..60ce6460d0ac2 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -7,11 +7,15 @@ import fs from 'fs/promises'; +import apm from 'elastic-apm-node'; + import { compact } from 'lodash'; import pMap from 'p-map'; import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; +import type { UninstallTokenError } from '../../common/errors'; + import { AUTO_UPDATE_PACKAGES } from '../../common/constants'; import type { PreconfigurationError } from '../../common/constants'; import type { DefaultPackagesInstallationError } from '../../common/types'; @@ -53,7 +57,10 @@ import { cleanUpOldFileIndices } from './setup/clean_old_fleet_indices'; export interface SetupStatus { isInitialized: boolean; nonFatalErrors: Array< - PreconfigurationError | DefaultPackagesInstallationError | UpgradeManagedPackagePoliciesResult + | PreconfigurationError + | DefaultPackagesInstallationError + | UpgradeManagedPackagePoliciesResult + | { error: UninstallTokenError } >; } @@ -61,7 +68,17 @@ export async function setupFleet( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient ): Promise { - return awaitIfPending(async () => createSetupSideEffects(soClient, esClient)); + const t = apm.startTransaction('fleet-setup', 'fleet'); + + try { + return await awaitIfPending(async () => createSetupSideEffects(soClient, esClient)); + } catch (error) { + apm.captureError(error); + t.setOutcome('failure'); + throw error; + } finally { + t.end(); + } } async function createSetupSideEffects( @@ -113,7 +130,9 @@ async function createSetupSideEffects( const defaultOutput = await outputService.ensureDefaultOutput(soClient, esClient); logger.debug('Setting up Fleet Elasticsearch assets'); + let stepSpan = apm.startSpan('Install Fleet global assets', 'preconfiguration'); await ensureFleetGlobalEsAssets(soClient, esClient); + stepSpan?.end(); // Ensure that required packages are always installed even if they're left out of the config const preconfiguredPackageNames = new Set(packages.map((pkg) => pkg.name)); @@ -136,6 +155,7 @@ async function createSetupSideEffects( logger.debug('Setting up initial Fleet packages'); + stepSpan = apm.startSpan('Install preconfigured packages and policies', 'preconfiguration'); const { nonFatalErrors: preconfiguredPackagesNonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( soClient, @@ -146,17 +166,23 @@ async function createSetupSideEffects( defaultDownloadSource, DEFAULT_SPACE_ID ); + stepSpan?.end(); + stepSpan = apm.startSpan('Upgrade managed package policies', 'preconfiguration'); const packagePolicyUpgradeErrors = ( await upgradeManagedPackagePolicies(soClient, esClient) ).filter((result) => (result.errors ?? []).length > 0); + stepSpan?.end(); const nonFatalErrors = [...preconfiguredPackagesNonFatalErrors, ...packagePolicyUpgradeErrors]; logger.debug('Upgrade Fleet package install versions'); + stepSpan = apm.startSpan('Upgrade package install format version', 'preconfiguration'); await upgradePackageInstallVersion({ soClient, esClient, logger }); + stepSpan?.end(); logger.debug('Generating key pair for message signing'); + stepSpan = apm.startSpan('Configure message signing', 'preconfiguration'); if (!appContextService.getMessageSigningService()?.isEncryptionAvailable) { logger.warn( 'xpack.encryptedSavedObjects.encryptionKey is not configured, private key passphrase is being stored in plain text' @@ -177,15 +203,33 @@ async function createSetupSideEffects( await appContextService.getUninstallTokenService()?.encryptTokens(); } + logger.debug('Checking validity of Uninstall Tokens'); + try { + await appContextService.getUninstallTokenService()?.checkTokenValidityForAllPolicies(); + } catch (error) { + nonFatalErrors.push({ error }); + } + stepSpan?.end(); + + stepSpan = apm.startSpan('Upgrade agent policy schema', 'preconfiguration'); + logger.debug('Upgrade Agent policy schema version'); await upgradeAgentPolicySchemaVersion(soClient); + stepSpan?.end(); + stepSpan = apm.startSpan('Set up enrollment keys for preconfigured policies', 'preconfiguration'); logger.debug('Setting up Fleet enrollment keys'); await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); + stepSpan?.end(); if (nonFatalErrors.length > 0) { logger.info('Encountered non fatal errors during Fleet setup'); - formatNonFatalErrors(nonFatalErrors).forEach((error) => logger.info(JSON.stringify(error))); + formatNonFatalErrors(nonFatalErrors) + .map((e) => JSON.stringify(e)) + .forEach((error) => { + logger.info(error); + apm.captureError(error); + }); } logger.info('Fleet setup completed'); @@ -222,6 +266,7 @@ export async function ensureFleetGlobalEsAssets( esClient, installation, }).catch((err) => { + apm.captureError(err); logger.error( `Package needs to be manually reinstalled ${installation.name} after installing Fleet global assets: ${err.message}` ); diff --git a/x-pack/plugins/fleet/server/services/setup_utils.ts b/x-pack/plugins/fleet/server/services/setup_utils.ts index 4ac77e8e7d60f..a9801e3470b76 100644 --- a/x-pack/plugins/fleet/server/services/setup_utils.ts +++ b/x-pack/plugins/fleet/server/services/setup_utils.ts @@ -12,7 +12,7 @@ let isPending = false; let onResolve = (value?: unknown) => {}; let onReject = (reason: any) => {}; -export async function awaitIfPending(asyncFunction: Function): Promise { +export async function awaitIfPending(asyncFunction: () => Promise): Promise { // pending successful or failed attempt if (isPending) { // don't run concurrent installs diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index b9dc7528651eb..d56c92eb7aa8b 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -41,6 +41,7 @@ export type { Installation, EpmPackageInstallStatus, InstallationStatus, + InstallFailedAttempt, PackageInfo, ArchivePackage, RegistryVarsEntry, diff --git a/x-pack/plugins/fleet/server/types/models/output.ts b/x-pack/plugins/fleet/server/types/models/output.ts index d77663b65e784..4c391e8383a58 100644 --- a/x-pack/plugins/fleet/server/types/models/output.ts +++ b/x-pack/plugins/fleet/server/types/models/output.ts @@ -130,13 +130,23 @@ const ElasticSearchUpdateSchema = { export const RemoteElasticSearchSchema = { ...ElasticSearchSchema, type: schema.literal(outputType.RemoteElasticsearch), - service_token: schema.string(), + service_token: schema.maybe(schema.string()), + secrets: schema.maybe( + schema.object({ + service_token: schema.maybe(secretRefSchema), + }) + ), }; const RemoteElasticSearchUpdateSchema = { ...ElasticSearchUpdateSchema, type: schema.maybe(schema.literal(outputType.RemoteElasticsearch)), service_token: schema.maybe(schema.string()), + secrets: schema.maybe( + schema.object({ + service_token: schema.maybe(secretRefSchema), + }) + ), }; /** diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 66974744d4adb..df98f0b00a424 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -154,7 +154,9 @@ interface OutputSoElasticsearchAttributes extends OutputSoBaseAttributes { export interface OutputSoRemoteElasticsearchAttributes extends OutputSoBaseAttributes { type: OutputType['RemoteElasticsearch']; service_token?: string; - secrets?: {}; + secrets?: { + service_token?: { id: string }; + }; } interface OutputSoLogstashAttributes extends OutputSoBaseAttributes { diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 4d3c850b9e75d..4503d328e1503 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -102,5 +102,6 @@ "@kbn/dashboard-plugin", "@kbn/cloud", "@kbn/config", + "@kbn/core-http-server-mocks", ] } diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts index a41f3d71bc6bc..efe9630a5f238 100644 --- a/x-pack/plugins/index_management/common/constants/index.ts +++ b/x-pack/plugins/index_management/common/constants/index.ts @@ -10,6 +10,10 @@ export { API_BASE_PATH, INTERNAL_API_BASE_PATH } from './api_base_path'; export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters'; export * from './index_statuses'; +// Since each index can have a max length or 255 characters and the max length of +// the request is 4096 bytes we can fit a max of 16 indices in a single request. +export const MAX_INDICES_PER_REQUEST = 16; + export { UIM_APP_NAME, UIM_APP_LOAD, diff --git a/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js b/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js index 09c4988fc27b1..f27c184afea68 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js @@ -27,8 +27,9 @@ export const clearCacheIndices = dispatch(reloadIndices(indexNames)); notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.clearCacheIndicesAction.successMessage', { - defaultMessage: 'Successfully cleared cache: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: + 'Successfully cleared cache for {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); }; diff --git a/x-pack/plugins/index_management/public/application/store/actions/close_indices.js b/x-pack/plugins/index_management/public/application/store/actions/close_indices.js index a9bedef283ec4..368298a67137e 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/close_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/close_indices.js @@ -25,8 +25,8 @@ export const closeIndices = dispatch(reloadIndices(indexNames)); notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.closeIndicesAction.successfullyClosedIndicesMessage', { - defaultMessage: 'Successfully closed: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: 'Successfully closed {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); }; diff --git a/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js b/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js index 2a2f8f0b41092..1b082594f085a 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js @@ -23,8 +23,8 @@ export const deleteIndices = } notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage', { - defaultMessage: 'Successfully deleted: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: 'Successfully deleted {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); dispatch(deleteIndicesSuccess({ indexNames })); diff --git a/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js b/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js index 83d4d5d46a3ae..d033f5fbe343e 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js @@ -26,8 +26,8 @@ export const flushIndices = dispatch(reloadIndices(indexNames)); notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.flushIndicesAction.successfullyFlushedIndicesMessage', { - defaultMessage: 'Successfully flushed: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: 'Successfully flushed {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); }; diff --git a/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js b/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js index bb3af529f404a..d589ff6dd780f 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js @@ -28,8 +28,9 @@ export const forcemergeIndices = i18n.translate( 'xpack.idxMgmt.forceMergeIndicesAction.successfullyForceMergedIndicesMessage', { - defaultMessage: 'Successfully force merged: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: + 'Successfully force merged {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, } ) ); diff --git a/x-pack/plugins/index_management/public/application/store/actions/open_indices.js b/x-pack/plugins/index_management/public/application/store/actions/open_indices.js index 8d4d0f728d160..53bb9186c2f94 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/open_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/open_indices.js @@ -26,8 +26,8 @@ export const openIndices = dispatch(reloadIndices(indexNames)); notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.openIndicesAction.successfullyOpenedIndicesMessage', { - defaultMessage: 'Successfully opened: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: 'Successfully opened {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); }; diff --git a/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js b/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js index 56fe892393ea4..574aa18c0282c 100644 --- a/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js +++ b/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js @@ -26,8 +26,8 @@ export const refreshIndices = dispatch(reloadIndices(indexNames)); notificationService.showSuccessToast( i18n.translate('xpack.idxMgmt.refreshIndicesAction.successfullyRefreshedIndicesMessage', { - defaultMessage: 'Successfully refreshed: [{indexNames}]', - values: { indexNames: indexNames.join(', ') }, + defaultMessage: 'Successfully refreshed {count, plural, one {# index} other {# indices} }', + values: { count: indexNames.length }, }) ); }; diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_datastream_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_datastream_route.ts index b117f7b8f9ba4..8a7f1917a4218 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_datastream_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_datastream_route.ts @@ -31,7 +31,7 @@ async function getDatastreamsForComponentTemplate( const { index_templates: indexTemplates } = await esClient.indices.getIndexTemplate(); const datastreamNames = indexTemplates - .filter((indexTemplate) => indexTemplate.index_template.composed_of.includes(name)) + .filter((indexTemplate) => indexTemplate.index_template?.composed_of?.includes(name)) .map((indexTemplate) => indexTemplate.index_template.index_patterns) .flat() .join(','); @@ -39,6 +39,7 @@ async function getDatastreamsForComponentTemplate( if (datastreamNames.length < 0) { return []; } + const { data_streams: dataStreams } = await esClient.indices.getDataStream({ name: datastreamNames, }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/helpers.test.ts b/x-pack/plugins/index_management/server/routes/api/indices/helpers.test.ts new file mode 100644 index 0000000000000..fd44742d26e4c --- /dev/null +++ b/x-pack/plugins/index_management/server/routes/api/indices/helpers.test.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 type { IScopedClusterClient } from '@kbn/core/server'; + +import { executeAsyncByChunks } from './helpers'; + +const generateIndices = (count: number) => { + const indices = []; + + for (let i = 0; i < count; i++) { + indices.push(`index-${i}`); + } + + return indices; +}; + +const mockClient = { + asCurrentUser: { + indices: { + delete: jest.fn(), + }, + }, +} as unknown as IScopedClusterClient; + +describe('executeAsyncByChunks', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should make just one request for one index', async () => { + const params = { + index: generateIndices(1), + }; + + await executeAsyncByChunks(params, mockClient, 'delete'); + + expect(mockClient.asCurrentUser.indices.delete).toHaveBeenCalledTimes(1); + }); + + it('should make 2 requests for 32 indices', async () => { + const params = { + index: generateIndices(32), + }; + + await executeAsyncByChunks(params, mockClient, 'delete'); + + expect(mockClient.asCurrentUser.indices.delete).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/helpers.ts b/x-pack/plugins/index_management/server/routes/api/indices/helpers.ts new file mode 100644 index 0000000000000..bb04cbd2c15c8 --- /dev/null +++ b/x-pack/plugins/index_management/server/routes/api/indices/helpers.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { chunk } from 'lodash'; + +import type { IScopedClusterClient } from '@kbn/core/server'; +import { MAX_INDICES_PER_REQUEST } from '../../../../common/constants'; + +// To avoid having to to match method signatures with the client +// type, we use a generic CallableFn type. +type CallableFn = (args: Record) => Promise; + +export async function executeAsyncByChunks( + // Since we are using a key to access the index method, we need + // to use a generic type. + params: { + index: T[]; + format?: string; + expand_wildcards?: string; + max_num_segments?: number; + }, + dataClient: IScopedClusterClient, + methodName: keyof IScopedClusterClient['asCurrentUser']['indices'] +) { + const { index: indices, ...commonParams } = params; + + // When the number of indices is small, we can execute in a single request + // + // Otherwise we need to split the indices into chunks and execute them in multiple requests because + // if we try to execute an action with too many indices that account for a long string in the request + // ES will throw an error saying that the HTTP line is too large. + if (indices.length <= MAX_INDICES_PER_REQUEST) { + await (dataClient.asCurrentUser.indices[methodName] as CallableFn)({ + ...commonParams, + index: indices, + }); + } else { + const chunks = chunk(indices, MAX_INDICES_PER_REQUEST); + + await Promise.all( + chunks.map((chunkOfIndices) => + (dataClient.asCurrentUser.indices[methodName] as CallableFn)({ + ...commonParams, + index: chunkOfIndices, + }) + ) + ); + } +} diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts index a46a23b8fe479..bfedf6f4cb0cf 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -28,7 +29,8 @@ export function registerClearCacheRoute({ router, lib: { handleEsError } }: Rout }; try { - await client.asCurrentUser.indices.clearCache(params); + await executeAsyncByChunks(params, client, 'clearCache'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts index 69d33b7fc7999..b83c781f6457d 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -28,7 +29,7 @@ export function registerCloseRoute({ router, lib: { handleEsError } }: RouteDepe }; try { - await client.asCurrentUser.indices.close(params); + await executeAsyncByChunks(params, client, 'close'); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts index b72a2059beb1d..b3931c1d56172 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -22,13 +23,14 @@ export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDep const { indices = [] } = request.body as typeof bodySchema.type; const params = { - expand_wildcards: 'none' as const, format: 'json', + expand_wildcards: 'none' as const, index: indices, }; try { - await client.asCurrentUser.indices.delete(params); + await executeAsyncByChunks(params, client, 'delete'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts index cc07b92e70907..6ba8000306fec 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -28,7 +29,8 @@ export function registerFlushRoute({ router, lib: { handleEsError } }: RouteDepe }; try { - await client.asCurrentUser.indices.flush(params); + await executeAsyncByChunks(params, client, 'flush'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts index af07a7371cf65..ffbe50598f197 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -36,7 +37,8 @@ export function registerForcemergeRoute({ router, lib: { handleEsError } }: Rout } try { - await client.asCurrentUser.indices.forcemerge(params); + await executeAsyncByChunks(params, client, 'forcemerge'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts index dde9e72af39d7..9d0ae0a44b4ec 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -28,7 +29,8 @@ export function registerOpenRoute({ router, lib: { handleEsError } }: RouteDepen }; try { - await client.asCurrentUser.indices.open(params); + await executeAsyncByChunks(params, client, 'open'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts index 2483cd534b80e..c414a73cd73c1 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; +import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ indices: schema.arrayOf(schema.string()), @@ -28,7 +29,8 @@ export function registerRefreshRoute({ router, lib: { handleEsError } }: RouteDe }; try { - await client.asCurrentUser.indices.refresh(params); + await executeAsyncByChunks(params, client, 'refresh'); + return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts index 91a04187fc238..d64c6b1013d66 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts @@ -5,8 +5,10 @@ * 2.0. */ +import { chunk } from 'lodash'; import { schema } from '@kbn/config-schema'; +import { MAX_INDICES_PER_REQUEST } from '../../../../common/constants'; import { RouteDependencies } from '../../../types'; import { fetchIndices } from '../../../lib/fetch_indices'; import { addBasePath } from '..'; @@ -30,7 +32,27 @@ export function registerReloadRoute({ const { indexNames = [] } = (request.body as typeof bodySchema.type) ?? {}; try { - const indices = await fetchIndices({ client, indexDataEnricher, config, indexNames }); + let indices; + + // When the number of indices is small, we can execute in a single request + // + // Otherwise we need to split the indices into chunks and execute them in multiple requests because + // if we try to execute an action with too many indices that account for a long string in the request + // ES will throw an error saying that the HTTP line is too large. + if (indexNames.length <= MAX_INDICES_PER_REQUEST) { + indices = await fetchIndices({ client, indexDataEnricher, config, indexNames }); + } else { + const chunks = chunk(indexNames, MAX_INDICES_PER_REQUEST); + + indices = ( + await Promise.all( + chunks.map((indexNamesChunk) => + fetchIndices({ client, indexDataEnricher, config, indexNames: indexNamesChunk }) + ) + ) + ).flat(); + } + return response.ok({ body: indices }); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/infra/common/http_api/profiling_api.ts b/x-pack/plugins/infra/common/http_api/profiling_api.ts new file mode 100644 index 0000000000000..d6a27155477a9 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/profiling_api.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +export const InfraProfilingFlamegraphRequestParamsRT = rt.type({ + hostname: rt.string, + from: rt.number, + to: rt.number, +}); + +export const InfraProfilingFunctionsRequestParamsRT = rt.type({ + hostname: rt.string, + from: rt.number, + to: rt.number, + startIndex: rt.number, + endIndex: rt.number, +}); + +export type InfraProfilingFlamegraphRequestParams = rt.TypeOf< + typeof InfraProfilingFlamegraphRequestParamsRT +>; + +export type InfraProfilingFunctionsRequestParams = rt.TypeOf< + typeof InfraProfilingFunctionsRequestParamsRT +>; diff --git a/x-pack/plugins/infra/common/plugin_config_types.ts b/x-pack/plugins/infra/common/plugin_config_types.ts index 8f39680959bb5..b69afe176cab8 100644 --- a/x-pack/plugins/infra/common/plugin_config_types.ts +++ b/x-pack/plugins/infra/common/plugin_config_types.ts @@ -34,6 +34,7 @@ export interface InfraConfig { metricThresholdAlertRuleEnabled: boolean; logThresholdAlertRuleEnabled: boolean; alertsAndRulesDropdownEnabled: boolean; + profilingEnabled: boolean; }; } diff --git a/x-pack/plugins/infra/kibana.jsonc b/x-pack/plugins/infra/kibana.jsonc index b97a273b91f71..a5ffd146b618f 100644 --- a/x-pack/plugins/infra/kibana.jsonc +++ b/x-pack/plugins/infra/kibana.jsonc @@ -35,7 +35,7 @@ "usageCollection", "visTypeTimeseries" ], - "optionalPlugins": ["spaces", "ml", "home", "embeddable", "osquery", "cloud"], + "optionalPlugins": ["spaces", "ml", "home", "embeddable", "osquery", "cloud", "profilingDataAccess"], "requiredBundles": [ "unifiedSearch", "observability", diff --git a/x-pack/plugins/infra/public/common/asset_details_config/asset_details_tabs.tsx b/x-pack/plugins/infra/public/common/asset_details_config/asset_details_tabs.tsx index 09e5cf2684075..312ed7eb04a98 100644 --- a/x-pack/plugins/infra/public/common/asset_details_config/asset_details_tabs.tsx +++ b/x-pack/plugins/infra/public/common/asset_details_config/asset_details_tabs.tsx @@ -27,6 +27,12 @@ export const commonFlyoutTabs: Tab[] = [ defaultMessage: 'Processes', }), }, + { + id: ContentTabIds.PROFILING, + name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.profiling', { + defaultMessage: 'Universal Profiling', + }), + }, { id: ContentTabIds.LOGS, name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', { diff --git a/x-pack/plugins/infra/public/common/visualizations/constants.ts b/x-pack/plugins/infra/public/common/visualizations/constants.ts index 98fafda2e23ce..76b2eb7c3d70f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/constants.ts +++ b/x-pack/plugins/infra/public/common/visualizations/constants.ts @@ -7,3 +7,6 @@ export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics'; export const HOST_METRICS_DOTTED_LINES_DOC_HREF = 'https://ela.st/docs-infra-why-dotted'; + +export const KPI_CHART_HEIGHT = 150; +export const METRIC_CHART_HEIGHT = 300; diff --git a/x-pack/plugins/infra/public/common/visualizations/index.ts b/x-pack/plugins/infra/public/common/visualizations/index.ts index 35a8dc5120614..6bfe3f0df588b 100644 --- a/x-pack/plugins/infra/public/common/visualizations/index.ts +++ b/x-pack/plugins/infra/public/common/visualizations/index.ts @@ -5,5 +5,9 @@ * 2.0. */ -export * from './lens/dashboards'; -export * from './lens/formulas'; +export { METRICS_TOOLTIP } from './translations'; +export { + HOST_METRICS_DOC_HREF, + HOST_METRICS_DOTTED_LINES_DOC_HREF, + KPI_CHART_HEIGHT, +} from './constants'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_kpi_charts.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_kpi_charts.ts deleted file mode 100644 index 9805fd3176683..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_kpi_charts.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import { METRICS_TOOLTIP } from '../../translations'; -import type { KPIChartProps } from '../../types'; - -export const hostKPICharts: KPIChartProps[] = [ - { - id: 'cpuUsage', - title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.cpuUsage.title', { - defaultMessage: 'CPU Usage', - }), - layers: { - data: { - ...hostLensFormulas.cpuUsage, - format: hostLensFormulas.cpuUsage.format - ? { - ...hostLensFormulas.cpuUsage.format, - params: { - decimals: 1, - }, - } - : undefined, - }, - options: { - backgroundColor: '#F1D86F', - showTrendLine: true, - }, - type: 'visualization', - }, - toolTip: METRICS_TOOLTIP.cpuUsage, - }, - { - id: 'normalizedLoad1m', - title: i18n.translate( - 'xpack.infra.assetDetailsEmbeddable.overview.kpi.normalizedLoad1m.title', - { - defaultMessage: 'Normalized Load', - } - ), - layers: { - data: { - ...hostLensFormulas.normalizedLoad1m, - format: hostLensFormulas.normalizedLoad1m.format - ? { - ...hostLensFormulas.normalizedLoad1m.format, - params: { - decimals: 1, - }, - } - : undefined, - }, - options: { - backgroundColor: '#79AAD9', - showTrendLine: true, - }, - type: 'visualization', - }, - toolTip: METRICS_TOOLTIP.normalizedLoad1m, - }, - { - id: 'memoryUsage', - title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.memoryUsage.title', { - defaultMessage: 'Memory Usage', - }), - layers: { - data: { - ...hostLensFormulas.memoryUsage, - format: hostLensFormulas.memoryUsage.format - ? { - ...hostLensFormulas.memoryUsage.format, - params: { - decimals: 1, - }, - } - : undefined, - }, - options: { - backgroundColor: '#A987D1', - showTrendLine: true, - }, - type: 'visualization', - }, - toolTip: METRICS_TOOLTIP.memoryUsage, - }, - { - id: 'diskSpaceUsage', - title: i18n.translate('xpack.infra.assetDetailsEmbeddable.overview.kpi.diskUsage.title', { - defaultMessage: 'Disk Usage', - }), - layers: { - data: { - ...hostLensFormulas.diskUsage, - format: hostLensFormulas.diskUsage.format - ? { - ...hostLensFormulas.diskUsage.format, - params: { - decimals: 1, - }, - } - : undefined, - }, - options: { - backgroundColor: '#F5A35C', - showTrendLine: true, - }, - type: 'visualization', - }, - toolTip: METRICS_TOOLTIP.diskSpaceUsage, - }, -]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts deleted file mode 100644 index b4dfc1abf5ebf..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/host_metric_charts.ts +++ /dev/null @@ -1,44 +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 { cpuUsage, normalizedLoad1m, cpuUsageBreakdown, loadBreakdown } from '../metric_charts/cpu'; -import { - diskSpaceUsageAvailable, - diskThroughputReadWrite, - diskIOReadWrite, - diskUsageByMountPoint, -} from '../metric_charts/disk'; -import { logRate } from '../metric_charts/log'; -import { memoryUsage, memoryUsageBreakdown } from '../metric_charts/memory'; -import { rxTx } from '../metric_charts/network'; -import type { XYConfig } from '../../types'; - -export const hostMetricFlyoutCharts: XYConfig[] = [ - cpuUsage, - memoryUsage, - normalizedLoad1m, - logRate, - diskSpaceUsageAvailable, - diskUsageByMountPoint, - diskThroughputReadWrite, - diskIOReadWrite, - rxTx, -]; - -export const hostMetricChartsFullPage: XYConfig[] = [ - cpuUsage, - cpuUsageBreakdown, - memoryUsage, - memoryUsageBreakdown, - normalizedLoad1m, - loadBreakdown, - logRate, - diskSpaceUsageAvailable, - diskUsageByMountPoint, - diskThroughputReadWrite, - diskIOReadWrite, - rxTx, -]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/kubernetes_charts.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/kubernetes_charts.ts deleted file mode 100644 index 83ca15b4deb05..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/host/kubernetes_charts.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { kubernetesLensFormulas } from '../../../formulas'; -import { XY_OVERRIDES } from '../../constants'; -import type { XYConfig } from '../../types'; - -export const kubernetesCharts: XYConfig[] = [ - { - id: 'nodeCpuCapacity', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity', { - defaultMessage: 'Node CPU Capacity', - }), - - layers: [ - { - data: [kubernetesLensFormulas.nodeCpuCapacity, kubernetesLensFormulas.nodeCpuUsed], - type: 'visualization', - options: { - seriesType: 'area', - }, - }, - ], - dataViewOrigin: 'metrics', - overrides: { - settings: XY_OVERRIDES.settings, - }, - }, - { - id: 'nodeMemoryCapacity', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.nginx.nodeMemoryCapacity', { - defaultMessage: 'Node Memory Capacity', - }), - - layers: [ - { - data: [kubernetesLensFormulas.nodeMemoryCapacity, kubernetesLensFormulas.nodeMemoryUsed], - type: 'visualization', - options: { - seriesType: 'area', - }, - }, - ], - dataViewOrigin: 'metrics', - overrides: { - settings: XY_OVERRIDES.settings, - }, - }, - { - id: 'nodeDiskCapacity', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.nginx.nodeDiskCapacity', { - defaultMessage: 'Node Disk Capacity', - }), - - layers: [ - { - data: [kubernetesLensFormulas.nodeDiskCapacity, kubernetesLensFormulas.nodeDiskUsed], - type: 'visualization', - options: { - seriesType: 'area', - }, - }, - ], - dataViewOrigin: 'metrics', - overrides: { - settings: XY_OVERRIDES.settings, - }, - }, - { - id: 'nodePodCapacity', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.nginx.nodePodCapacity', { - defaultMessage: 'Node Pod Capacity', - }), - - layers: [ - { - data: [kubernetesLensFormulas.nodePodCapacity, kubernetesLensFormulas.nodePodUsed], - type: 'visualization', - options: { - seriesType: 'area', - }, - }, - ], - dataViewOrigin: 'metrics', - overrides: { - settings: XY_OVERRIDES.settings, - }, - }, -]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts deleted file mode 100644 index aef6787c088fc..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/index.ts +++ /dev/null @@ -1,19 +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 { hostMetricFlyoutCharts, hostMetricChartsFullPage } from './host/host_metric_charts'; -import { hostKPICharts } from './host/host_kpi_charts'; -import { kubernetesCharts } from './host/kubernetes_charts'; - -export const assetDetailsDashboards = { - host: { hostMetricFlyoutCharts, hostMetricChartsFullPage, hostKPICharts, keyField: 'host.name' }, - kubernetes: { - kubernetesCharts, - keyField: 'kubernetes.node.name', - dependsOn: ['kubernetes.node'], - }, -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts deleted file mode 100644 index 27925b29da722..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/cpu.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import { REFERENCE_LINE, XY_OVERRIDES } from '../../constants'; -import type { XYConfig } from '../../types'; - -export const cpuUsage: XYConfig = { - id: 'cpuUsage', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.cpuUsage', { - defaultMessage: 'CPU Usage', - }), - - layers: [ - { - data: [hostLensFormulas.cpuUsage], - type: 'visualization', - }, - ], - dataViewOrigin: 'metrics', - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - }, -}; - -export const cpuUsageBreakdown: XYConfig = { - id: 'cpuUsageBreakdown', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.cpuUsage', { - defaultMessage: 'CPU Usage', - }), - layers: [ - { - data: [ - hostLensFormulas.cpuUsageIowait, - hostLensFormulas.cpuUsageIrq, - hostLensFormulas.cpuUsageNice, - hostLensFormulas.cpuUsageSoftirq, - hostLensFormulas.cpuUsageSteal, - hostLensFormulas.cpuUsageUser, - hostLensFormulas.cpuUsageSystem, - ], - options: { - seriesType: 'area_stacked', - }, - type: 'visualization', - }, - ], - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; - -export const normalizedLoad1m: XYConfig = { - id: 'normalizedLoad1m', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.normalizedLoad1m', { - defaultMessage: 'Normalized Load', - }), - layers: [ - { - data: [hostLensFormulas.normalizedLoad1m], - type: 'visualization', - }, - { - data: [REFERENCE_LINE], - type: 'referenceLines', - }, - ], - dataViewOrigin: 'metrics', -}; - -export const loadBreakdown: XYConfig = { - id: 'loadBreakdown', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.load', { - defaultMessage: 'Load', - }), - layers: [ - { - data: [hostLensFormulas.load1m, hostLensFormulas.load5m, hostLensFormulas.load15m], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts deleted file mode 100644 index daf5ee2ecaac6..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/disk.ts +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import { XY_OVERRIDES } from '../../constants'; -import type { XYConfig } from '../../types'; - -const TOP_VALUES_SIZE = 5; - -export const diskSpaceUsageAvailable: XYConfig = { - id: 'diskSpaceUsageAvailable', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskUsage', { - defaultMessage: 'Disk Usage', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskUsage, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskUsage.label.used', { - defaultMessage: 'Used', - }), - }, - { - ...hostLensFormulas.diskSpaceAvailability, - label: i18n.translate( - 'xpack.infra.assetDetails.metricsCharts.diskUsage.label.available', - { - defaultMessage: 'Available', - } - ), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; - -export const diskUsageByMountPoint: XYConfig = { - id: 'DiskUsageByMountPoint', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskUsageByMountingPoint', { - defaultMessage: 'Disk Usage by Mount Point', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskUsage, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskUsage.label.used', { - defaultMessage: 'Used', - }), - }, - ], - options: { - seriesType: 'area', - breakdown: { - type: 'top_values', - field: 'system.filesystem.mount_point', - params: { - size: TOP_VALUES_SIZE, - }, - }, - }, - type: 'visualization', - }, - ], - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - }, - dataViewOrigin: 'metrics', -}; - -export const diskThroughputReadWrite: XYConfig = { - id: 'diskThroughputReadWrite', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskIOPS', { - defaultMessage: 'Disk IOPS', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskIORead, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { - defaultMessage: 'Read', - }), - }, - { - ...hostLensFormulas.diskIOWrite, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { - defaultMessage: 'Write', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; - -export const diskIOReadWrite: XYConfig = { - id: 'diskIOReadWrite', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.diskThroughput', { - defaultMessage: 'Disk Throughput', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.diskReadThroughput, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.read', { - defaultMessage: 'Read', - }), - }, - { - ...hostLensFormulas.diskWriteThroughput, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.write', { - defaultMessage: 'Write', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts deleted file mode 100644 index 267474c4363fa..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/log.ts +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import type { XYConfig } from '../../types'; - -export const logRate: XYConfig = { - id: 'logRate', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.logRate', { - defaultMessage: 'Log Rate', - }), - layers: [ - { - data: [hostLensFormulas.logRate], - type: 'visualization', - }, - ], - dataViewOrigin: 'logs', -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts deleted file mode 100644 index dae8d46832c01..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/memory.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import { XY_OVERRIDES } from '../../constants'; -import type { XYConfig } from '../../types'; - -export const memoryUsage: XYConfig = { - id: 'memoryUsage', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.memoryUsage', { - defaultMessage: 'Memory Usage', - }), - layers: [ - { - data: [hostLensFormulas.memoryUsage], - type: 'visualization', - }, - ], - dataViewOrigin: 'metrics', - overrides: { - axisLeft: XY_OVERRIDES.axisLeft, - }, -}; - -export const memoryUsageBreakdown: XYConfig = { - id: 'memoryUsage', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.memoryUsage', { - defaultMessage: 'Memory Usage', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.memoryCache, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.cache', { - defaultMessage: 'Cache', - }), - }, - { - ...hostLensFormulas.memoryUsed, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.used', { - defaultMessage: 'Used', - }), - }, - { - ...hostLensFormulas.memoryFreeExcludingCache, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.metric.label.free', { - defaultMessage: 'Free', - }), - }, - ], - options: { - seriesType: 'area_stacked', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts deleted file mode 100644 index aae8de6a96173..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/asset_details/metric_charts/network.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { hostLensFormulas } from '../../../formulas'; -import { XY_OVERRIDES } from '../../constants'; -import type { XYConfig } from '../../types'; - -export const rxTx: XYConfig = { - id: 'rxTx', - title: i18n.translate('xpack.infra.assetDetails.metricsCharts.network', { - defaultMessage: 'Network', - }), - layers: [ - { - data: [ - { - ...hostLensFormulas.rx, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.rx', { - defaultMessage: 'Inbound (RX)', - }), - }, - { - ...hostLensFormulas.tx, - label: i18n.translate('xpack.infra.assetDetails.metricsCharts.network.label.tx', { - defaultMessage: 'Outbound (TX)', - }), - }, - ], - options: { - seriesType: 'area', - }, - type: 'visualization', - }, - ], - overrides: { - settings: XY_OVERRIDES.settings, - }, - dataViewOrigin: 'metrics', -}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts deleted file mode 100644 index 5efc100ada739..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/constants.ts +++ /dev/null @@ -1,46 +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 { StaticValueConfig, XYVisualOptions } from '@kbn/lens-embeddable-utils'; -import type { AllowedSettingsOverrides, AllowedXYOverrides } from '@kbn/lens-plugin/common/types'; - -interface XYOverrides { - axisLeft: AllowedXYOverrides['axisLeft']; - settings: AllowedSettingsOverrides['settings']; -} - -export const REFERENCE_LINE: StaticValueConfig = { - value: '1', - format: { - id: 'percent', - params: { - decimals: 0, - }, - }, - color: '#6092c0', -}; - -export const XY_OVERRIDES: XYOverrides = { - axisLeft: { - domain: { - min: 0, - max: 1, - }, - }, - settings: { - showLegend: true, - legendPosition: 'bottom', - legendSize: 50, - }, -}; - -export const XY_MISSING_VALUE_DOTTED_LINE_CONFIG: XYVisualOptions = { - showDottedLine: true, - missingValues: 'Linear', -}; - -export const KPI_CHART_HEIGHT = 150; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/hosts_metric_charts.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/hosts_metric_charts.ts deleted file mode 100644 index d7b1898c321e0..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/hosts_metric_charts.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { XYLayerOptions } from '@kbn/lens-embeddable-utils'; -import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { hostLensFormulas } from '../../formulas'; -import type { XYChartLayerParams } from '../../../types'; -import { REFERENCE_LINE, XY_OVERRIDES } from '../constants'; - -const XY_LAYER_OPTIONS: XYLayerOptions = { - breakdown: { - type: 'top_values', - field: 'host.name', - params: { - size: 20, - }, - }, -}; - -export const hostsMetricCharts: Array< - Pick & { - layers: XYChartLayerParams[]; - } -> = [ - { - id: 'cpuUsage', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.cpuUsage', { - defaultMessage: 'CPU Usage', - }), - layers: [ - { data: [hostLensFormulas.cpuUsage], options: XY_LAYER_OPTIONS, type: 'visualization' }, - ], - overrides: { axisLeft: XY_OVERRIDES.axisLeft }, - }, - { - id: 'normalizedLoad1m', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.normalizedLoad1m', { - defaultMessage: 'Normalized Load', - }), - layers: [ - { - data: [hostLensFormulas.normalizedLoad1m], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - { - data: [REFERENCE_LINE], - type: 'referenceLines', - }, - ], - }, - { - id: 'memoryUsage', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.memoryUsage', { - defaultMessage: 'Memory Usage', - }), - layers: [ - { - data: [hostLensFormulas.memoryUsage], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - overrides: { axisLeft: XY_OVERRIDES.axisLeft }, - }, - { - id: 'memoryFree', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.memoryFree', { - defaultMessage: 'Memory Free', - }), - layers: [ - { - data: [hostLensFormulas.memoryFree], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'diskSpaceUsed', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskUsage', { - defaultMessage: 'Disk Usage', - }), - layers: [ - { - data: [hostLensFormulas.diskUsage], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - overrides: { axisLeft: XY_OVERRIDES.axisLeft }, - }, - { - id: 'diskSpaceAvailable', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceAvailable', { - defaultMessage: 'Disk Space Available', - }), - layers: [ - { - data: [hostLensFormulas.diskSpaceAvailable], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'diskIORead', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskIORead', { - defaultMessage: 'Disk Read IOPS', - }), - layers: [ - { - data: [hostLensFormulas.diskIORead], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'diskIOWrite', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskIOWrite', { - defaultMessage: 'Disk Write IOPS', - }), - layers: [ - { - data: [hostLensFormulas.diskIOWrite], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'diskReadThroughput', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskReadThroughput', { - defaultMessage: 'Disk Read Throughput', - }), - layers: [ - { - data: [hostLensFormulas.diskReadThroughput], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'diskWriteThroughput', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.diskWriteThroughput', { - defaultMessage: 'Disk Write Throughput', - }), - layers: [ - { - data: [hostLensFormulas.diskWriteThroughput], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'rx', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.rx', { - defaultMessage: 'Network Inbound (RX)', - }), - layers: [ - { - data: [hostLensFormulas.rx], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, - { - id: 'tx', - title: i18n.translate('xpack.infra.hostsViewPage.tabs.metricsCharts.tx', { - defaultMessage: 'Network Outbound (TX)', - }), - layers: [ - { - data: [hostLensFormulas.tx], - options: XY_LAYER_OPTIONS, - type: 'visualization', - }, - ], - }, -]; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/index.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/index.ts deleted file mode 100644 index 446c39158d8cc..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/index.ts +++ /dev/null @@ -1,12 +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 { assetDetailsDashboards } from './asset_details'; -export { hostsViewDashboards } from './hosts_view'; -export { AVERAGE_SUBTITLE, METRICS_TOOLTIP } from './translations'; -export { XY_MISSING_VALUE_DOTTED_LINE_CONFIG, KPI_CHART_HEIGHT } from './constants'; -export * from './types'; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/types.ts b/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/types.ts deleted file mode 100644 index 90899c11573e4..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/types.ts +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import type { DataViewOrigin } from '../../../../components/asset_details/types'; -import type { MetricChartLayerParams, XYChartLayerParams } from '../../types'; - -type BaseProps = Pick; - -export interface AssetXYChartProps extends BaseProps { - layers: XYChartLayerParams[]; -} - -export interface XYConfig extends AssetXYChartProps { - dataViewOrigin: DataViewOrigin; -} - -export interface KPIChartProps extends BaseProps { - layers: MetricChartLayerParams; - toolTip: string; -} diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/translations.ts b/x-pack/plugins/infra/public/common/visualizations/translations.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/dashboards/translations.ts rename to x-pack/plugins/infra/public/common/visualizations/translations.ts index c14674f7d7f40..c29f4e49d9eba 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/translations.ts +++ b/x-pack/plugins/infra/public/common/visualizations/translations.ts @@ -16,7 +16,7 @@ export const METRICS_TOOLTIP = { defaultMessage: 'Percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. This includes both time spent on user space and kernel space.', }), - diskSpaceUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskSpaceUsage', { + diskUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskSpaceUsage', { defaultMessage: 'Percentage of disk space used.', }), diskLatency: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskLatency', { @@ -43,10 +43,3 @@ export const METRICS_TOOLTIP = { 'Number of bytes which have been sent per second on the public interfaces of the hosts.', }), }; - -export const AVERAGE_SUBTITLE = i18n.translate( - 'xpack.infra.assetDetailsEmbeddable.overview.kpi.subtitle.average', - { - defaultMessage: 'Average', - } -); diff --git a/x-pack/plugins/infra/public/common/visualizations/types.ts b/x-pack/plugins/infra/public/common/visualizations/types.ts deleted file mode 100644 index 301908cec9527..0000000000000 --- a/x-pack/plugins/infra/public/common/visualizations/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - MetricLayerConfig, - XYLayerConfig, - XYReferenceLinesLayerConfig, -} from '@kbn/lens-embeddable-utils'; - -export type XYChartLayerParams = - | (XYLayerConfig & { type: 'visualization' }) - | (XYReferenceLinesLayerConfig & { type: 'referenceLines' }); - -export type MetricChartLayerParams = MetricLayerConfig & { type: 'visualization' }; diff --git a/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx b/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx index e779808347ec4..e55a70978a748 100644 --- a/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx @@ -190,6 +190,7 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { metricThresholdAlertRuleEnabled: true, logThresholdAlertRuleEnabled: true, alertsAndRulesDropdownEnabled: true, + profilingEnabled: false, }, }; diff --git a/x-pack/plugins/infra/public/components/asset_details/constants.ts b/x-pack/plugins/infra/public/components/asset_details/constants.ts index 3375c76bcfd3d..4e3ed09e1d46b 100644 --- a/x-pack/plugins/infra/public/components/asset_details/constants.ts +++ b/x-pack/plugins/infra/public/components/asset_details/constants.ts @@ -10,7 +10,6 @@ import { INTEGRATION_NAME } from './types'; export const ASSET_DETAILS_FLYOUT_COMPONENT_NAME = 'infraAssetDetailsFlyout'; export const ASSET_DETAILS_PAGE_COMPONENT_NAME = 'infraAssetDetailsPage'; -export const METRIC_CHART_HEIGHT = 300; export const APM_HOST_FILTER_FIELD = 'host.hostname'; export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails'; diff --git a/x-pack/plugins/infra/public/components/asset_details/content/content.tsx b/x-pack/plugins/infra/public/components/asset_details/content/content.tsx index 9ffd94fcd9e75..49ad1cb87398b 100644 --- a/x-pack/plugins/infra/public/components/asset_details/content/content.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/content/content.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { DatePicker } from '../date_picker/date_picker'; import { useTabSwitcherContext } from '../hooks/use_tab_switcher'; -import { Anomalies, Metadata, Processes, Osquery, Logs, Overview } from '../tabs'; +import { Anomalies, Metadata, Processes, Osquery, Logs, Overview, Profiling } from '../tabs'; import { ContentTabIds } from '../types'; export const Content = () => { @@ -22,6 +22,7 @@ export const Content = () => { ContentTabIds.LOGS, ContentTabIds.METADATA, ContentTabIds.PROCESSES, + ContentTabIds.PROFILING, ContentTabIds.ANOMALIES, ]} /> @@ -45,6 +46,9 @@ export const Content = () => { + + +
    ); diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts index 4b5ec03b3300b..42abaf1c5bc91 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts @@ -49,6 +49,7 @@ const TabIdRT = rt.union([ rt.literal(ContentTabIds.OVERVIEW), rt.literal(ContentTabIds.METADATA), rt.literal(ContentTabIds.PROCESSES), + rt.literal(ContentTabIds.PROFILING), rt.literal(ContentTabIds.LOGS), rt.literal(ContentTabIds.ANOMALIES), rt.literal(ContentTabIds.OSQUERY), diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx index a7e0b311d3343..3fae1eca66a40 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx @@ -113,8 +113,9 @@ const useFeatureFlagTabs = () => { const featureFlagControlledTabs: Partial> = useMemo( () => ({ [ContentTabIds.OSQUERY]: featureFlags.osqueryEnabled, + [ContentTabIds.PROFILING]: featureFlags.profilingEnabled, }), - [featureFlags.osqueryEnabled] + [featureFlags.osqueryEnabled, featureFlags.profilingEnabled] ); const isTabEnabled = useCallback( diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_flamegraph_data.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_flamegraph_data.ts new file mode 100644 index 0000000000000..872e1ee9ef0d4 --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_flamegraph_data.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useMemo } from 'react'; +import type { BaseFlameGraph } from '@kbn/profiling-utils'; +import { type InfraProfilingFlamegraphRequestParams } from '../../../../common/http_api/profiling_api'; +import { useHTTPRequest } from '../../../hooks/use_http_request'; +import { useRequestObservable } from './use_request_observable'; + +interface Props { + params: InfraProfilingFlamegraphRequestParams; + isActive: boolean; +} + +export function useProfilingFlamegraphData({ params, isActive }: Props) { + const { request$ } = useRequestObservable(); + const fetchOptions = useMemo(() => ({ query: params }), [params]); + const { loading, error, response, makeRequest } = useHTTPRequest( + `/api/infra/profiling/flamegraph`, + 'GET', + undefined, + undefined, + undefined, + undefined, + true, + fetchOptions + ); + + useEffect(() => { + if (!isActive) { + return; + } + + request$.next(makeRequest); + }, [isActive, makeRequest, request$]); + + return { + loading, + error, + response, + }; +} diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_functions_data.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_functions_data.ts new file mode 100644 index 0000000000000..20d780528c304 --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_profiling_functions_data.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useMemo } from 'react'; +import type { TopNFunctions } from '@kbn/profiling-utils'; +import { type InfraProfilingFunctionsRequestParams } from '../../../../common/http_api/profiling_api'; +import { useHTTPRequest } from '../../../hooks/use_http_request'; +import { useRequestObservable } from './use_request_observable'; + +interface Props { + params: InfraProfilingFunctionsRequestParams; + isActive: boolean; +} + +export function useProfilingFunctionsData({ params, isActive }: Props) { + const { request$ } = useRequestObservable(); + const fetchOptions = useMemo(() => ({ query: params }), [params]); + const { loading, error, response, makeRequest } = useHTTPRequest( + '/api/infra/profiling/functions', + 'GET', + undefined, + undefined, + undefined, + undefined, + true, + fetchOptions + ); + + useEffect(() => { + if (!isActive) { + return; + } + + request$.next(makeRequest); + }, [isActive, makeRequest, request$]); + + return { + loading, + error, + response, + }; +} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/index.ts b/x-pack/plugins/infra/public/components/asset_details/tabs/index.ts index 4a3cde85a3ca2..779531d15910b 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/index.ts +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/index.ts @@ -8,6 +8,7 @@ export { Anomalies } from './anomalies/anomalies'; export { Metadata } from './metadata/metadata'; export { Processes } from './processes/processes'; +export { Profiling } from './profiling/profiling'; export { Osquery } from './osquery/osquery'; export { Logs } from './logs/logs'; export { Overview } from './overview/overview'; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi.tsx index f586aeaab7630..4047d365b8f24 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi.tsx @@ -8,21 +8,20 @@ import React, { useMemo } from 'react'; import type { DataView } from '@kbn/data-views-plugin/public'; import { TimeRange } from '@kbn/es-query'; +import { ChartModel } from '@kbn/lens-embeddable-utils'; +import { METRICS_TOOLTIP } from '../../../../../common/visualizations'; import { LensChart, TooltipContent } from '../../../../lens'; -import { AVERAGE_SUBTITLE, type KPIChartProps } from '../../../../../common/visualizations'; import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; import { useLoadingStateContext } from '../../../hooks/use_loading_state'; export const Kpi = ({ id, - title, - layers, - toolTip, height, - dataView, assetName, dateRange, -}: KPIChartProps & { + dataView, + ...chartProps +}: ChartModel & { height: number; dataView?: DataView; assetName: string; @@ -39,20 +38,23 @@ export const Kpi = ({ ]; }, [dataView, assetName]); - const tooltipContent = useMemo(() => , [toolTip]); + const tooltipContent = useMemo( + () => + id in METRICS_TOOLTIP ? ( + + ) : undefined, + [id] + ); return ( { + const model = findInventoryModel('host'); + const { euiTheme } = useEuiTheme(); + + const { value: dashboards } = useAsync(() => { + return model.metrics.getDashboards(); + }); + + const charts = useMemo( + () => + dashboards?.kpi.get({ + metricsDataView: dataView, + options: { + backgroundColor: euiTheme.colors.lightestShade, + }, + }).charts ?? [], + [dataView, euiTheme.colors.lightestShade, dashboards?.kpi] + ); return ( - {assetDetailsDashboards.host.hostKPICharts.map((chartProps, index) => ( + {charts.map((chartProps, index) => ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx index 72d7f27c7dc48..d1e3bc1bdee3a 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx @@ -5,47 +5,37 @@ * 2.0. */ import React, { useCallback, useMemo } from 'react'; -import type { XYVisualOptions } from '@kbn/lens-embeddable-utils'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import type { ChartModel } from '@kbn/lens-embeddable-utils'; import type { TimeRange } from '@kbn/es-query'; +import { METRIC_CHART_HEIGHT } from '../../../../../common/visualizations/constants'; import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; -import { type BrushEndArgs, LensChart, type OnFilterEvent } from '../../../../lens'; -import { METRIC_CHART_HEIGHT } from '../../../constants'; +import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../../../lens'; import { useDatePickerContext } from '../../../hooks/use_date_picker'; import { extractRangeFromChartFilterEvent } from './chart_utils'; -import type { XYConfig } from '../../../../../common/visualizations'; import { useLoadingStateContext } from '../../../hooks/use_loading_state'; -export interface ChartProps extends XYConfig { - visualOptions?: XYVisualOptions; - metricsDataView?: DataView; - logsDataView?: DataView; - filterFieldName: string; - dateRange: TimeRange; - assetName: string; - ['data-test-subj']: string; -} +export type ChartProps = ChartModel & + Pick & { + filterFieldName: string; + dateRange: TimeRange; + assetName: string; + dataViewOrigin?: 'metrics' | 'logs'; + ['data-test-subj']: string; + }; export const Chart = ({ id, - title, - layers, - metricsDataView, - logsDataView, filterFieldName, - visualOptions, dataViewOrigin, overrides, dateRange, assetName, + dataView, ...props }: ChartProps) => { const { setDateRange } = useDatePickerContext(); const { searchSessionId } = useLoadingStateContext(); - - const dataView = useMemo(() => { - return dataViewOrigin === 'metrics' ? metricsDataView : logsDataView; - }, [dataViewOrigin, logsDataView, metricsDataView]); + const { ['data-test-subj']: dataTestSubj, ...chartProps } = { ...props }; const filters = useMemo(() => { return [ @@ -85,18 +75,15 @@ export const Chart = ({ return ( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx index 1050ff72a246c..d2697112cd0d0 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx @@ -4,61 +4,31 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo } from 'react'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import React from 'react'; import { EuiFlexItem, EuiFlexGrid } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; -import { - type XYConfig, - XY_MISSING_VALUE_DOTTED_LINE_CONFIG, -} from '../../../../../common/visualizations'; -import { useMetadataStateContext } from '../../../hooks/use_metadata_state'; +import type { ChartModel } from '@kbn/lens-embeddable-utils'; import { Chart } from './chart'; interface Props { assetName: string; dateRange: TimeRange; - metricsDataView?: DataView; - logsDataView?: DataView; filterFieldName: string; - charts: Array; + charts: ChartModel[]; ['data-test-subj']: string; } -export const MetricsGrid = ({ - assetName, - metricsDataView, - logsDataView, - dateRange, - filterFieldName, - charts, - ...props -}: Props) => { - const { metadata } = useMetadataStateContext(); - - const chartsToRender = useMemo( - () => - charts.filter( - (c) => - !c.dependsOn || - c.dependsOn.every((d) => (metadata?.features ?? []).some((f) => d === f.name)) - ), - [charts, metadata?.features] - ); - +export const MetricsGrid = ({ assetName, dateRange, filterFieldName, charts, ...props }: Props) => { return ( - {chartsToRender.map((chartProp, index) => ( + {charts.map((chartProp, index) => ( ))} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx index f6baa4038c230..3750b5bf422a7 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx @@ -10,7 +10,8 @@ import { EuiFlexItem } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { TimeRange } from '@kbn/es-query'; import { EuiFlexGroup } from '@elastic/eui'; -import { assetDetailsDashboards } from '../../../../../common/visualizations'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import useAsync from 'react-use/lib/useAsync'; import { MetricsSectionTitle, KubernetesMetricsSectionTitle, @@ -25,31 +26,45 @@ interface Props { logsDataView?: DataView; } -const { host, kubernetes } = assetDetailsDashboards; - export const MetricsSection = ({ assetName, metricsDataView, logsDataView, dateRange }: Props) => { + const model = findInventoryModel('host'); + + const { value } = useAsync(() => { + return model.metrics.getDashboards(); + }); + + const dashboards = useMemo( + () => ({ + hosts: value?.assetDetails.get({ + metricsDataView, + logsDataView, + }), + kubernetes: value?.assetDetailsKubernetesNode.get({ + metricsDataView, + }), + }), + + [logsDataView, metricsDataView, value?.assetDetails, value?.assetDetailsKubernetesNode] + ); + return (
    -
    +
    @@ -61,19 +76,33 @@ export const MetricsSectionCompact = ({ metricsDataView, logsDataView, dateRange, -}: Props) => ( -
    - -
    -); +}: Props) => { + const model = findInventoryModel('host'); + const { value } = useAsync(() => { + return model.metrics.getDashboards(); + }); + + const charts = useMemo( + () => + value?.assetDetailsFlyout.get({ + metricsDataView, + logsDataView, + }).charts ?? [], + [metricsDataView, logsDataView, value?.assetDetailsFlyout] + ); + + return ( +
    + +
    + ); +}; const Section = ({ title, diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/error_prompt.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/error_prompt.tsx new file mode 100644 index 0000000000000..ea18987869b1f --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/error_prompt.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiEmptyPrompt } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export function ErrorPrompt() { + return ( + + {i18n.translate('xpack.infra.profiling.loadErrorTitle', { + defaultMessage: 'Unable to load the Profiling data', + })} + + } + body={ +

    + {i18n.translate('xpack.infra.profiling.loadErrorBody', { + defaultMessage: + 'There was an error while trying to load profiling data. Try refreshing the page', + })} +

    + } + /> + ); +} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx new file mode 100644 index 0000000000000..cf00b72c74248 --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EmbeddableFlamegraph } from '@kbn/observability-shared-plugin/public'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; +import { useDatePickerContext } from '../../hooks/use_date_picker'; +import { useProfilingFlamegraphData } from '../../hooks/use_profiling_flamegraph_data'; +import { useTabSwitcherContext } from '../../hooks/use_tab_switcher'; +import { ContentTabIds } from '../../types'; +import { ErrorPrompt } from './error_prompt'; + +export function Flamegraph() { + const { asset } = useAssetDetailsRenderPropsContext(); + const { activeTabId } = useTabSwitcherContext(); + const { getDateRangeInTimestamp } = useDatePickerContext(); + const { from, to } = getDateRangeInTimestamp(); + + const params = useMemo( + () => ({ + hostname: asset.name, + from, + to, + }), + [asset.name, from, to] + ); + const { error, loading, response } = useProfilingFlamegraphData({ + isActive: activeTabId === ContentTabIds.PROFILING, + params, + }); + + if (error !== null) { + return ; + } + + return ; +} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx new file mode 100644 index 0000000000000..faece71e65448 --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EmbeddableFunctions } from '@kbn/observability-shared-plugin/public'; +import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; +import { useDatePickerContext } from '../../hooks/use_date_picker'; +import { useProfilingFunctionsData } from '../../hooks/use_profiling_functions_data'; +import { useTabSwitcherContext } from '../../hooks/use_tab_switcher'; +import { ContentTabIds } from '../../types'; +import { ErrorPrompt } from './error_prompt'; + +export function Functions() { + const { asset } = useAssetDetailsRenderPropsContext(); + const { activeTabId } = useTabSwitcherContext(); + const { getDateRangeInTimestamp } = useDatePickerContext(); + const { from, to } = getDateRangeInTimestamp(); + + const params = useMemo( + () => ({ + hostname: asset.name, + from, + to, + startIndex: 0, + endIndex: 10, + }), + [asset.name, from, to] + ); + + const { error, loading, response } = useProfilingFunctionsData({ + isActive: activeTabId === ContentTabIds.PROFILING, + params, + }); + + if (error !== null) { + return ; + } + + return ( + + ); +} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/profiling.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/profiling.tsx new file mode 100644 index 0000000000000..b89ef9fa42cdf --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/profiling.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +import { EuiSpacer, EuiTabbedContent, type EuiTabbedContentProps } from '@elastic/eui'; +import React from 'react'; +import { Flamegraph } from './flamegraph'; +import { Functions } from './functions'; + +export function Profiling() { + const tabs: EuiTabbedContentProps['tabs'] = [ + { + id: 'flamegraph', + name: i18n.translate('xpack.infra.profiling.flamegraphTabName', { + defaultMessage: 'Flamegraph', + }), + content: ( + <> + + + + ), + }, + { + id: 'functions', + name: i18n.translate('xpack.infra.tabs.profiling.functionsTabName', { + defaultMessage: 'Top 10 Functions', + }), + content: ( + <> + + + + ), + }, + ]; + + return ( + <> + + + ); +} diff --git a/x-pack/plugins/infra/public/components/asset_details/types.ts b/x-pack/plugins/infra/public/components/asset_details/types.ts index 56f62a5ebc086..dd3ae9af9d62e 100644 --- a/x-pack/plugins/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/infra/public/components/asset_details/types.ts @@ -21,6 +21,7 @@ export enum ContentTabIds { OVERVIEW = 'overview', METADATA = 'metadata', PROCESSES = 'processes', + PROFILING = 'profiling', ANOMALIES = 'anomalies', OSQUERY = 'osquery', LOGS = 'logs', diff --git a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx index 43d4e89135646..1b9980bf5f173 100644 --- a/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx +++ b/x-pack/plugins/infra/public/containers/plugin_config_context.test.tsx @@ -28,6 +28,7 @@ describe('usePluginConfig()', () => { metricThresholdAlertRuleEnabled: true, logThresholdAlertRuleEnabled: true, alertsAndRulesDropdownEnabled: true, + profilingEnabled: false, }, }; const { result } = renderHook(() => usePluginConfig(), { diff --git a/x-pack/plugins/infra/public/hooks/use_http_request.tsx b/x-pack/plugins/infra/public/hooks/use_http_request.tsx index 8196677004714..370d4a9ccf38c 100644 --- a/x-pack/plugins/infra/public/hooks/use_http_request.tsx +++ b/x-pack/plugins/infra/public/hooks/use_http_request.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { HttpHandler, ToastInput } from '@kbn/core/public'; +import { HttpFetchOptions, HttpHandler, ToastInput } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; import { useTrackedPromise, CanceledPromiseError } from '../utils/use_tracked_promise'; @@ -20,7 +20,8 @@ export function useHTTPRequest( decode: (response: any) => Response = (response) => response, fetch?: HttpHandler, toastDanger?: (input: ToastInput) => void, - abortable = false + abortable = false, + fetchOptions?: Omit ) { const kibana = useKibana(); const fetchService = fetch ? fetch : kibana.services.http?.fetch; @@ -100,6 +101,7 @@ export function useHTTPRequest( signal: abortController.current.signal, method, body, + ...fetchOptions, }); }, onResolve: (resp) => { @@ -114,7 +116,7 @@ export function useHTTPRequest( onError(e); }, }, - [pathname, body, method, fetch, toast, onError] + [pathname, body, method, fetch, toast, onError, fetchOptions] ); const loading = request.state === 'uninitialized' || request.state === 'pending'; diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts index 116107a64a43b..a0bf9dd6c1fcd 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.test.ts @@ -15,7 +15,6 @@ import { CoreStart } from '@kbn/core/public'; import type { InfraClientStartDeps } from '../types'; import { lensPluginMock } from '@kbn/lens-plugin/public/mocks'; import { FilterStateStore } from '@kbn/es-query'; -import { hostLensFormulas } from '../common/visualizations'; jest.mock('@kbn/kibana-react-plugin/public'); const useKibanaMock = useKibana as jest.MockedFunction; @@ -31,7 +30,16 @@ const mockDataView = { metaFields: [], } as unknown as jest.Mocked; -const normalizedLoad1m = hostLensFormulas.normalizedLoad1m; +const normalizedLoad1m = { + label: 'Normalized Load', + value: 'average(system.load.1) / max(system.load.cores)', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, +}; const lensPluginMockStart = lensPluginMock.createStartContract(); const mockUseKibana = () => { @@ -55,7 +63,6 @@ describe('useHostTable hook', () => { layers: [ { data: [normalizedLoad1m], - type: 'visualization', options: { buckets: { type: 'date_histogram', @@ -68,6 +75,7 @@ describe('useHostTable hook', () => { }, }, }, + layerType: 'data', }, { data: [ @@ -81,7 +89,7 @@ describe('useHostTable hook', () => { }, }, ], - type: 'referenceLines', + layerType: 'referenceLine', }, ], title: 'Injected Normalized Load', @@ -195,11 +203,12 @@ describe('useHostTable hook', () => { it('should return extra actions', async () => { const { result, waitForNextUpdate } = renderHook(() => useLensAttributes({ + title: 'Chart', visualizationType: 'lnsXY', layers: [ { data: [normalizedLoad1m], - type: 'visualization', + layerType: 'data', }, ], dataView: mockDataView, diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts index aa5feb4dc0b47..7a5f07d171eb7 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts @@ -15,9 +15,10 @@ import useAsync from 'react-use/lib/useAsync'; import { FormulaPublicApi } from '@kbn/lens-plugin/public'; import { type LensVisualizationState, - type XYVisualOptions, type Chart, type LensAttributes, + type ChartModel, + type XYLayerConfig, LensAttributesBuilder, XYChart, MetricChart, @@ -26,35 +27,17 @@ import { XYReferenceLinesLayer, } from '@kbn/lens-embeddable-utils'; import { InfraClientSetupDeps } from '../types'; -import type { MetricChartLayerParams, XYChartLayerParams } from '../common/visualizations/types'; -interface UseLensAttributesBaseParams { - dataView?: DataView; - title?: string; -} - -export interface UseLensAttributesXYChartParams extends UseLensAttributesBaseParams { - layers: XYChartLayerParams[]; - visualizationType: 'lnsXY'; - visualOptions?: XYVisualOptions; -} - -export interface UseLensAttributesMetricChartParams extends UseLensAttributesBaseParams { - layers: MetricChartLayerParams; - visualizationType: 'lnsMetric'; - subtitle?: string; -} - -export type UseLensAttributesParams = - | UseLensAttributesXYChartParams - | UseLensAttributesMetricChartParams; +export type UseLensAttributesParams = Omit; export const useLensAttributes = ({ dataView, ...params }: UseLensAttributesParams) => { const { services: { lens }, } = useKibana(); const { navigateToPrefilledEditor } = lens; - const { value, error } = useAsync(lens.stateHelperApi, [lens]); + const { value, error } = useAsync(() => { + return lens.stateHelperApi(); + }, [lens]); const { formula: formulaAPI } = value ?? {}; const attributes = useMemo(() => { @@ -144,7 +127,7 @@ export const useLensAttributes = ({ dataView, ...params }: UseLensAttributesPara const getFormula = () => { const firstDataLayer = [ ...(Array.isArray(params.layers) ? params.layers : [params.layers]), - ].find((p) => p.type === 'visualization'); + ].find((p) => p.layerType === 'data'); if (!firstDataLayer) { return ''; @@ -174,32 +157,27 @@ const chartFactory = ({ throw new Error(`Invalid layers type. Expected an array of layers.`); } - const xyLayerFactory = (layer: XYChartLayerParams) => { - switch (layer.type) { - case 'visualization': { - return new XYDataLayer({ - data: layer.data, - options: layer.options, - }); + const xyLayerFactory = (layer: XYLayerConfig) => { + switch (layer.layerType) { + case 'data': { + return new XYDataLayer(layer); } - case 'referenceLines': { - return new XYReferenceLinesLayer({ - data: layer.data, - }); + case 'referenceLine': { + return new XYReferenceLinesLayer(layer); } default: throw new Error(`Invalid layer type`); } }; + const { layers, ...rest } = params; return new XYChart({ dataView, formulaAPI, - layers: params.layers.map((layerItem) => { + layers: layers.map((layerItem) => { return xyLayerFactory(layerItem); }), - title: params.title, - visualOptions: params.visualOptions, + ...rest, }); case 'lnsMetric': @@ -212,7 +190,8 @@ const chartFactory = ({ formulaAPI, layers: new MetricLayer({ data: params.layers.data, - options: { ...params.layers.options, subtitle: params.subtitle }, + options: { ...params.layers.options }, + layerType: params.layers.layerType, }), title: params.title, }); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_content.tsx index 7dea5303aad28..5496dcf64cd50 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_content.tsx @@ -15,6 +15,7 @@ import { HostsViewProvider } from '../hooks/use_hosts_view'; import { HostsTableProvider } from '../hooks/use_hosts_table'; import { ErrorCallout } from './error_callout'; import { useUnifiedSearchContext } from '../hooks/use_unified_search'; +import { HostCountProvider } from '../hooks/use_host_count'; export const HostsContent = () => { const { error } = useUnifiedSearchContext(); @@ -28,7 +29,9 @@ export const HostsContent = () => { - + + + diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/host_count_kpi.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/host_count_kpi.tsx index 22f07d8299063..8d4794aaa6c44 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/host_count_kpi.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/host_count_kpi.tsx @@ -6,24 +6,30 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { hostLensFormulas, METRICS_TOOLTIP } from '../../../../../common/visualizations'; +import { useTheme } from '@kbn/observability-shared-plugin/public'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import useAsync from 'react-use/lib/useAsync'; +import { METRICS_TOOLTIP } from '../../../../../common/visualizations'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; - import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper'; import { TooltipContent } from '../../../../../components/lens'; -const HOSTS_CHART: Omit = { - id: 'hostsViewKPI-hostsCount', - color: '#6DCCB1', - title: i18n.translate('xpack.infra.hostsViewPage.kpi.hostCount.title', { - defaultMessage: 'Hosts', - }), -}; - export const HostCountKpi = ({ height }: { height: number }) => { + const inventoryModel = findInventoryModel('host'); const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); const { searchCriteria } = useUnifiedSearchContext(); + const euiTheme = useTheme(); + + const { value: formulas } = useAsync(() => inventoryModel.metrics.getFormulas()); + + const hostsCountChart: Omit = { + id: 'hostsViewKPI-hostsCount', + color: euiTheme.eui.euiColorLightestShade, + title: i18n.translate('xpack.infra.hostsViewPage.kpi.hostCount.title', { + defaultMessage: 'Hosts', + }), + }; const getSubtitle = () => { return searchCriteria.limit < (hostCountData?.count.value ?? 0) @@ -38,13 +44,13 @@ export const HostCountKpi = ({ height }: { height: number }) => { return ( } diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi.tsx index d57f28b6b71ed..e14d72ac7207d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi.tsx @@ -5,21 +5,25 @@ * 2.0. */ import React, { useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; +import { ChartModel } from '@kbn/lens-embeddable-utils'; +import { METRICS_TOOLTIP } from '../../../../../common/visualizations'; import { LensChart, TooltipContent } from '../../../../../components/lens'; -import { type KPIChartProps, AVERAGE_SUBTITLE } from '../../../../../common/visualizations'; import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; -import { useMetricsDataViewContext } from '../../hooks/use_data_view'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import { useHostsViewContext } from '../../hooks/use_hosts_view'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useAfterLoadedState } from '../../hooks/use_after_loaded_state'; -export const Kpi = ({ id, title, layers, toolTip, height }: KPIChartProps & { height: number }) => { +export const Kpi = ({ + id, + height, + visualizationType = 'lnsMetric', + dataView, + ...chartProps +}: ChartModel & { height: number }) => { const { searchCriteria } = useUnifiedSearchContext(); - const { dataView } = useMetricsDataViewContext(); const { hostNodes, loading: hostsLoading, searchSessionId } = useHostsViewContext(); - const { data: hostCountData, isRequestRunning: hostCountLoading } = useHostCountContext(); + const { isRequestRunning: hostCountLoading } = useHostCountContext(); const shouldUseSearchCriteria = hostNodes.length === 0; const loading = hostsLoading || hostCountLoading; @@ -34,16 +38,6 @@ export const Kpi = ({ id, title, layers, toolTip, height }: KPIChartProps & { he }), ]; - const subtitle = - searchCriteria.limit < (hostCountData?.count.value ?? 0) - ? i18n.translate('xpack.infra.hostsViewPage.kpi.subtitle.average.limit', { - defaultMessage: 'Average (of {limit} hosts)', - values: { - limit: searchCriteria.limit, - }, - }) - : AVERAGE_SUBTITLE; - // prevents requestTs and searchCriteria state from reloading the chart // we want it to reload only once the table has finished loading. // attributes passed to useAfterLoadedState don't need to be memoized @@ -52,26 +46,29 @@ export const Kpi = ({ id, title, layers, toolTip, height }: KPIChartProps & { he query: shouldUseSearchCriteria ? searchCriteria.query : undefined, filters, searchSessionId, - subtitle, }); - const tooltipComponent = useMemo(() => , [toolTip]); + const tooltipContent = useMemo( + () => + id in METRICS_TOOLTIP ? ( + + ) : undefined, + [id] + ); return ( diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx index d9c7a59eb9421..055793f6b53c7 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/kpi_grid.tsx @@ -4,31 +4,68 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; - +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import { useEuiTheme } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import useAsync from 'react-use/lib/useAsync'; +import { KPI_CHART_HEIGHT } from '../../../../../common/visualizations'; import { HostMetricsDocsLink } from '../../../../../components/lens'; import { Kpi } from './kpi'; -import { HostCountProvider } from '../../hooks/use_host_count'; +import { useHostCountContext } from '../../hooks/use_host_count'; import { HostCountKpi } from './host_count_kpi'; -import { assetDetailsDashboards, KPI_CHART_HEIGHT } from '../../../../../common/visualizations'; +import { useMetricsDataViewContext } from '../../hooks/use_data_view'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; export const KPIGrid = () => { + const model = findInventoryModel('host'); + const { searchCriteria } = useUnifiedSearchContext(); + const { euiTheme } = useEuiTheme(); + const { dataView } = useMetricsDataViewContext(); + const { data: hostCountData } = useHostCountContext(); + + const { value: dashboards } = useAsync(() => { + return model.metrics.getDashboards(); + }); + + const subtitle = + searchCriteria.limit < (hostCountData?.count.value ?? 0) + ? i18n.translate('xpack.infra.hostsViewPage.kpi.subtitle.average.limit', { + defaultMessage: 'Average (of {limit} hosts)', + values: { + limit: searchCriteria.limit, + }, + }) + : undefined; + + const charts = useMemo( + () => + dashboards?.kpi.get({ + metricsDataView: dataView, + options: { + backgroundColor: euiTheme.colors.lightestShade, + ...(subtitle ? { subtitle } : {}), + }, + }).charts ?? [], + [dashboards?.kpi, dataView, euiTheme.colors.lightestShade, subtitle] + ); + return ( - + <> - {assetDetailsDashboards.host.hostKPICharts.map((chartProp, index) => ( + {charts.map((chartProp, index) => ( ))} - + ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx index d7af2f06d4b08..1d82f0d76da22 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx @@ -5,24 +5,21 @@ * 2.0. */ import React, { useMemo } from 'react'; -import type { XYVisualOptions } from '@kbn/lens-embeddable-utils'; -import type { AssetXYChartProps } from '../../../../../../common/visualizations'; +import type { ChartModel, XYVisualOptions } from '@kbn/lens-embeddable-utils'; +import { METRIC_CHART_HEIGHT } from '../../../../../../common/visualizations/constants'; import { LensChart } from '../../../../../../components/lens'; -import { useMetricsDataViewContext } from '../../../hooks/use_data_view'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; import { buildCombinedHostsFilter } from '../../../../../../utils/filters/build'; import { useHostsTableContext } from '../../../hooks/use_hosts_table'; import { useAfterLoadedState } from '../../../hooks/use_after_loaded_state'; -import { METRIC_CHART_HEIGHT } from '../../../constants'; -export interface ChartProps extends AssetXYChartProps { +export interface ChartProps extends Omit { visualOptions?: XYVisualOptions; } -export const Chart = ({ id, title, layers, visualOptions, overrides }: ChartProps) => { +export const Chart = ({ dataView, ...chartProps }: ChartProps) => { const { searchCriteria } = useUnifiedSearchContext(); - const { dataView } = useMetricsDataViewContext(); const { loading, searchSessionId } = useHostsViewContext(); const { currentPage } = useHostsTableContext(); @@ -51,20 +48,16 @@ export const Chart = ({ id, title, layers, visualOptions, overrides }: ChartProp return ( ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx index d9310ae0d816b..37424447d9d72 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx @@ -4,22 +4,38 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGrid, EuiFlexItem, EuiText, EuiFlexGroup, EuiSpacer } from '@elastic/eui'; -import { - hostsViewDashboards, - XY_MISSING_VALUE_DOTTED_LINE_CONFIG, -} from '../../../../../../common/visualizations'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import useAsync from 'react-use/lib/useAsync'; import { HostMetricsExplanationContent } from '../../../../../../components/lens'; import { Chart } from './chart'; import { Popover } from '../../table/popover'; +import { useMetricsDataViewContext } from '../../../hooks/use_data_view'; export const MetricsGrid = () => { + const model = findInventoryModel('host'); + const { dataView } = useMetricsDataViewContext(); + + const { value: dashboards } = useAsync(() => { + return model.metrics.getDashboards(); + }); + + const charts = useMemo( + () => dashboards?.hostsView.get({ metricsDataView: dataView }).charts ?? [], + [dataView, dashboards] + ); + return ( <> - Learn more about metrics + + {i18n.translate('xpack.infra.metricsGrid.learnMoreAboutMetricsTextLabel', { + defaultMessage: 'Learn more about metrics', + })} + @@ -30,9 +46,9 @@ export const MetricsGrid = () => { - {hostsViewDashboards.hostsMetricCharts.map((chartProp, index) => ( + {charts.map((chartProp, index) => ( - + ))} diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts index 997ae6fd5ed66..cba547f072a64 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/constants.ts @@ -12,7 +12,5 @@ export const DEFAULT_PAGE_SIZE = 10; export const LOCAL_STORAGE_HOST_LIMIT_KEY = 'hostsView:hostLimitSelection'; export const LOCAL_STORAGE_PAGE_SIZE_KEY = 'hostsView:pageSizeSelection'; -export const METRIC_CHART_HEIGHT = 300; - export const HOST_LIMIT_OPTIONS = [50, 100, 500] as const; export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics'; 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 c1c4093d96e46..23aee3a6c1e3c 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 @@ -13,10 +13,11 @@ import { type EuiBasicTable, } from '@elastic/eui'; import createContainer from 'constate'; +import useAsync from 'react-use/lib/useAsync'; import { isEqual } from 'lodash'; import { isNumber } from 'lodash/fp'; import { CloudProvider } from '@kbn/custom-icons'; -import { hostLensFormulas } from '../../../../common/visualizations'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; import { EntryTitle } from '../components/table/entry_title'; @@ -124,9 +125,12 @@ const sortTableData = * Build a table columns and items starting from the snapshot nodes. */ export const useHostsTable = () => { + const inventoryModel = findInventoryModel('host'); const [selectedItems, setSelectedItems] = useState([]); const { hostNodes } = useHostsViewContext(); + const { value: formulas } = useAsync(() => inventoryModel.metrics.getFormulas()); + const [{ detailsItemId, pagination, sorting }, setProperties] = useHostsTableUrlState(); const { services: { @@ -241,7 +245,7 @@ export const useHostsTable = () => { ), field: 'cpu', @@ -255,7 +259,7 @@ export const useHostsTable = () => { ), field: 'normalizedLoad1m', @@ -269,7 +273,7 @@ export const useHostsTable = () => { ), field: 'memory', @@ -283,7 +287,7 @@ export const useHostsTable = () => { ), field: 'memoryFree', @@ -296,8 +300,8 @@ export const useHostsTable = () => { name: ( ), field: 'diskSpaceUsage', @@ -311,7 +315,7 @@ export const useHostsTable = () => { ), field: 'rx', @@ -326,7 +330,7 @@ export const useHostsTable = () => { ), field: 'tx', @@ -337,7 +341,18 @@ export const useHostsTable = () => { width: '120px', }, ], - [detailsItemId, reportHostEntryClick, setProperties] + [ + detailsItemId, + formulas?.cpuUsage.value, + formulas?.diskUsage.value, + formulas?.memoryFree.value, + formulas?.memoryUsage.value, + formulas?.normalizedLoad1m.value, + formulas?.rx.value, + formulas?.tx.value, + reportHostEntryClick, + setProperties, + ] ); const selection: EuiTableSelectionType = { diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts index 7f10c7aa4aa7d..36433a2bca016 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts @@ -19,6 +19,7 @@ export const getFilteredMetrics = ( .map((data) => data && data.name); return requiredMetrics.filter((metric) => { const metricModelCreator = metrics.tsvb[metric]; + // We just need to get a dummy version of the model so we can filter // using the `requires` attribute. const metricModel = metricModelCreator(TIMESTAMP_FIELD, 'test', '>=1m'); diff --git a/x-pack/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts index 66c65428833ac..b041f495057af 100644 --- a/x-pack/plugins/infra/server/infra_server.ts +++ b/x-pack/plugins/infra/server/infra_server.ts @@ -32,6 +32,7 @@ import { initProcessListRoute } from './routes/process_list'; import { initSnapshotRoute } from './routes/snapshot'; import { initInfraMetricsRoute } from './routes/infra'; import { initMetricsExplorerViewRoutes } from './routes/metrics_explorer_views'; +import { initProfilingRoutes } from './routes/profiling'; export const initInfraServer = (libs: InfraBackendLibs) => { initIpToHostName(libs); @@ -59,4 +60,5 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initProcessListRoute(libs); initOverviewRoute(libs); initInfraMetricsRoute(libs); + initProfilingRoutes(libs); }; diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 986a6a8749511..a6bba3babfa83 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -27,6 +27,10 @@ import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; import { LogsSharedPluginSetup, LogsSharedPluginStart } from '@kbn/logs-shared-plugin/server'; import { VersionedRouteConfig } from '@kbn/core-http-server'; import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server'; +import { + ProfilingDataAccessPluginSetup, + ProfilingDataAccessPluginStart, +} from '@kbn/profiling-data-access-plugin/server'; export interface InfraServerPluginSetupDeps { alerting: AlertingPluginContract; @@ -42,12 +46,14 @@ export interface InfraServerPluginSetupDeps { ml?: MlPluginSetup; logsShared: LogsSharedPluginSetup; metricsDataAccess: MetricsDataPluginSetup; + profilingDataAccess?: ProfilingDataAccessPluginSetup; } export interface InfraServerPluginStartDeps { data: DataPluginStart; dataViews: DataViewsPluginStart; logsShared: LogsSharedPluginStart; + profilingDataAccess?: ProfilingDataAccessPluginStart; } export interface CallWithRequestParams extends estypes.RequestBase { diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts index 37a39d215eddd..a62f5d92dac06 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts @@ -25,7 +25,8 @@ export const createConditionScript = ( } if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { return { - source: `params.value < params.threshold0 && params.value > params.threshold1 ? 1 : 0`, + // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], threshold1: threshold[1], diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts index b4285863dbccb..1320607685a87 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts @@ -18,7 +18,8 @@ export const createConditionScript = (threshold: number[], comparator: Comparato } if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { return { - source: `params.value < params.threshold0 && params.value > params.threshold1 ? 1 : 0`, + // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], threshold1: threshold[1], 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 81974728acc54..2c88148bc2fa4 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 @@ -2272,6 +2272,7 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({ metricThresholdAlertRuleEnabled: true, logThresholdAlertRuleEnabled: true, alertsAndRulesDropdownEnabled: true, + profilingEnabled: false, }, enabled: true, sources, diff --git a/x-pack/plugins/infra/server/lib/sources/sources.test.ts b/x-pack/plugins/infra/server/lib/sources/sources.test.ts index 27f232eed3c99..ef58e5a985778 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.test.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.test.ts @@ -101,6 +101,7 @@ const createMockStaticConfiguration = (): InfraConfig => ({ metricThresholdAlertRuleEnabled: true, logThresholdAlertRuleEnabled: true, alertsAndRulesDropdownEnabled: true, + profilingEnabled: false, }, enabled: true, }); diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index afcbe69c01f5c..63bb40f66c32d 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -112,6 +112,15 @@ export const config: PluginConfigDescriptor = { traditional: schema.boolean({ defaultValue: true }), serverless: schema.boolean({ defaultValue: true }), }), + /** + * This flag depends on profilingDataAccess optional plugin, + * make sure to enable it with `xpack.profiling.enabled: true` + * before enabling this flag. + */ + profilingEnabled: offeringBasedSchema({ + traditional: schema.boolean({ defaultValue: false }), + serverless: schema.boolean({ defaultValue: false }), + }), }), }), exposeToBrowser: publicConfigKeys, diff --git a/x-pack/plugins/infra/server/routes/profiling/index.ts b/x-pack/plugins/infra/server/routes/profiling/index.ts new file mode 100644 index 0000000000000..d322fcca37b51 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/profiling/index.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { + InfraProfilingFlamegraphRequestParamsRT, + InfraProfilingFunctionsRequestParamsRT, +} from '../../../common/http_api/profiling_api'; +import type { InfraBackendLibs } from '../../lib/infra_types'; +import { fetchProfilingFlamegraph } from './lib/fetch_profiling_flamegraph'; +import { fetchProfilingFunctions } from './lib/fetch_profiling_functions'; +import { fetchProfilingStatus } from './lib/fetch_profiling_status'; +import { getProfilingDataAccess } from './lib/get_profiling_data_access'; + +const CACHE_CONTROL_HEADER_VALUE = 'private, max-age=3600'; + +export function initProfilingRoutes({ framework, getStartServices, logger }: InfraBackendLibs) { + if (!Object.hasOwn(framework.plugins, 'profilingDataAccess')) { + logger.info( + "Skipping initialization of Profiling endpoints because 'profilingDataAccess' plugin is not available" + ); + return; + } + + framework.registerRoute( + { + method: 'get', + path: '/api/infra/profiling/status', + validate: false, + }, + async (requestContext, request, response) => { + const [coreRequestContext, infraRequestContext, profilingDataAccess] = await Promise.all([ + requestContext.core, + requestContext.infra, + getProfilingDataAccess(getStartServices), + ]); + + const profilingStatus = await fetchProfilingStatus( + profilingDataAccess, + coreRequestContext, + infraRequestContext + ); + + return response.ok({ + body: profilingStatus, + }); + } + ); + + framework.registerRoute( + { + method: 'get', + path: '/api/infra/profiling/flamegraph', + validate: { + query: schema.object({ + hostname: schema.string(), + from: schema.number(), + to: schema.number(), + }), + }, + }, + async (requestContext, request, response) => { + const params = decodeOrThrow(InfraProfilingFlamegraphRequestParamsRT)(request.query); + + const [coreRequestContext, profilingDataAccess] = await Promise.all([ + requestContext.core, + getProfilingDataAccess(getStartServices), + ]); + + const flamegraph = await fetchProfilingFlamegraph( + params, + profilingDataAccess, + coreRequestContext + ); + + return response.ok({ + body: flamegraph, + headers: { + 'cache-control': CACHE_CONTROL_HEADER_VALUE, + }, + }); + } + ); + + framework.registerRoute( + { + method: 'get', + path: '/api/infra/profiling/functions', + validate: { + query: schema.object({ + hostname: schema.string(), + from: schema.number(), + to: schema.number(), + startIndex: schema.number(), + endIndex: schema.number(), + }), + }, + }, + async (requestContext, request, response) => { + const params = decodeOrThrow(InfraProfilingFunctionsRequestParamsRT)(request.query); + + const [coreRequestContext, profilingDataAccess] = await Promise.all([ + requestContext.core, + getProfilingDataAccess(getStartServices), + ]); + + const functions = await fetchProfilingFunctions( + params, + profilingDataAccess, + coreRequestContext + ); + + return response.ok({ + body: functions, + headers: { + 'cache-control': CACHE_CONTROL_HEADER_VALUE, + }, + }); + } + ); +} diff --git a/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_flamegraph.ts b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_flamegraph.ts new file mode 100644 index 0000000000000..70c9c9b0ebd0f --- /dev/null +++ b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_flamegraph.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreRequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import type { ProfilingDataAccessPluginStart } from '@kbn/profiling-data-access-plugin/server'; +import type { BaseFlameGraph } from '@kbn/profiling-utils'; +import { profilingUseLegacyFlamegraphAPI } from '@kbn/observability-plugin/common'; +import type { InfraProfilingFlamegraphRequestParams } from '../../../../common/http_api/profiling_api'; +import { HOST_FIELD } from '../../../../common/constants'; + +export async function fetchProfilingFlamegraph( + { hostname, from, to }: InfraProfilingFlamegraphRequestParams, + profilingDataAccess: ProfilingDataAccessPluginStart, + coreRequestContext: CoreRequestHandlerContext +): Promise { + const useLegacyFlamegraphAPI = await coreRequestContext.uiSettings.client.get( + profilingUseLegacyFlamegraphAPI + ); + + return await profilingDataAccess.services.fetchFlamechartData({ + esClient: coreRequestContext.elasticsearch.client.asCurrentUser, + rangeFromMs: from, + rangeToMs: to, + kuery: `${HOST_FIELD} : "${hostname}"`, + useLegacyFlamegraphAPI, + }); +} diff --git a/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_functions.ts b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_functions.ts new file mode 100644 index 0000000000000..9b119a1d81e69 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_functions.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreRequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import type { ProfilingDataAccessPluginStart } from '@kbn/profiling-data-access-plugin/server'; +import type { TopNFunctions } from '@kbn/profiling-utils'; +import { HOST_FIELD } from '../../../../common/constants'; +import type { InfraProfilingFunctionsRequestParams } from '../../../../common/http_api/profiling_api'; + +export async function fetchProfilingFunctions( + params: InfraProfilingFunctionsRequestParams, + profilingDataAccess: ProfilingDataAccessPluginStart, + coreRequestContext: CoreRequestHandlerContext +): Promise { + const { hostname, from, to, startIndex, endIndex } = params; + + return await profilingDataAccess.services.fetchFunction({ + esClient: coreRequestContext.elasticsearch.client.asCurrentUser, + rangeFromMs: from, + rangeToMs: to, + kuery: `${HOST_FIELD} : "${hostname}"`, + startIndex, + endIndex, + }); +} diff --git a/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_status.ts b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_status.ts new file mode 100644 index 0000000000000..3ef929eeef5a9 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/profiling/lib/fetch_profiling_status.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 type { CoreRequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import type { ProfilingDataAccessPluginStart } from '@kbn/profiling-data-access-plugin/server'; +import type { ProfilingStatus } from '@kbn/profiling-utils'; +import type { InfraRequestHandlerContext } from '../../../types'; + +export async function fetchProfilingStatus( + profilingDataAccess: ProfilingDataAccessPluginStart, + coreRequestContext: CoreRequestHandlerContext, + infraRequestContext: InfraRequestHandlerContext +): Promise { + return await profilingDataAccess.services.getStatus({ + esClient: coreRequestContext.elasticsearch.client, + soClient: coreRequestContext.savedObjects.client, + spaceId: infraRequestContext.spaceId, + }); +} diff --git a/x-pack/plugins/infra/server/routes/profiling/lib/get_profiling_data_access.ts b/x-pack/plugins/infra/server/routes/profiling/lib/get_profiling_data_access.ts new file mode 100644 index 0000000000000..2e54b38b4c804 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/profiling/lib/get_profiling_data_access.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 type { ProfilingDataAccessPluginStart } from '@kbn/profiling-data-access-plugin/server'; +import type { InfraPluginStartServicesAccessor } from '../../../types'; + +export async function getProfilingDataAccess( + getStartServices: InfraPluginStartServicesAccessor +): Promise { + const [, { profilingDataAccess }] = await getStartServices(); + + if (profilingDataAccess === undefined) { + throw new Error( + "Trying to access profilingDataAccess plugin but it's not in the start dependencies" + ); + } + + return profilingDataAccess; +} diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index ea4fa7be6565f..da026aa7d7e93 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -75,7 +75,10 @@ "@kbn/chart-icons", "@kbn/advanced-settings-plugin", "@kbn/cloud-plugin", - "@kbn/custom-icons" + "@kbn/custom-icons", + "@kbn/profiling-utils", + "@kbn/profiling-data-access-plugin", + "@kbn/core-http-request-handler-context-server" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts index d54cc4de89fa7..267671a062655 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable_column.ts @@ -45,6 +45,7 @@ export interface ColumnState { summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max'; summaryLabel?: string; collapseFn?: CollapseFunction; + isMetric?: boolean; } export type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' }; diff --git a/x-pack/plugins/lens/kibana.jsonc b/x-pack/plugins/lens/kibana.jsonc index 04bb96af59388..1813264f7ca57 100644 --- a/x-pack/plugins/lens/kibana.jsonc +++ b/x-pack/plugins/lens/kibana.jsonc @@ -56,6 +56,7 @@ "embeddable", "fieldFormats", "charts", + "textBasedLanguages", ], "extraPublicDirs": [ "common/constants" diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 9f5a9eb6d5a46..83cc485e09c76 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -538,9 +538,10 @@ describe('Lens App', () => { } async function testSave(inst: ReactWrapper, saveProps: SaveProps) { - await getButton(inst).run(inst.getDOMNode()); - inst.update(); - const handler = inst.find('SavedObjectSaveModalOrigin').prop('onSave') as ( + getButton(inst).run(inst.getDOMNode()); + // wait a tick since SaveModalContainer initializes asynchronously + await new Promise(process.nextTick); + const handler = inst.update().find('SavedObjectSaveModalOrigin').prop('onSave') as ( p: unknown ) => void; handler(saveProps); diff --git a/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx b/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx index e01f68d9e4b30..83ae6cd66702c 100644 --- a/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx +++ b/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { isFilterPinned } from '@kbn/es-query'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import type { SavedObjectReference } from '@kbn/core/public'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { SaveModal } from './save_modal'; import type { LensAppProps, LensAppServices } from './types'; import type { SaveProps } from './app'; @@ -58,6 +59,7 @@ export function SaveModalContainer({ let title = ''; let description; let savedObjectId; + const [initializing, setInitializing] = useState(true); const [lastKnownDoc, setLastKnownDoc] = useState(initLastKnownDoc); if (lastKnownDoc) { title = lastKnownDoc.title; @@ -92,9 +94,15 @@ export function SaveModalContainer({ getPersisted({ initialInput, lensServices, - }).then((persisted) => { - if (persisted?.doc && isMounted) setLastKnownDoc(persisted.doc); - }); + }) + .then((persisted) => { + if (persisted?.doc && isMounted) setLastKnownDoc(persisted.doc); + }) + .finally(() => { + setInitializing(false); + }); + } else { + setInitializing(false); } return () => { @@ -135,6 +143,10 @@ export function SaveModalContainer({ } }; + if (initializing) { + return ; + } + const savingToLibraryPermitted = Boolean(isSaveable && application.capabilities.visualize.save); return ( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx new file mode 100644 index 0000000000000..5f0abbf3a952f --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx @@ -0,0 +1,157 @@ +/* + * Copyright 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 { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, + EuiToolTip, + EuiButton, + EuiLink, + EuiBetaBadge, +} from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { FlyoutWrapperProps } from './types'; + +export const FlyoutWrapper = ({ + children, + isInlineFlyoutVisible, + isScrollable, + displayFlyoutHeader, + language, + attributesChanged, + onCancel, + navigateToLensEditor, + onApply, +}: FlyoutWrapperProps) => { + return ( + <> + {isInlineFlyoutVisible && displayFlyoutHeader && ( + + + + +

    + {i18n.translate('xpack.lens.config.editVisualizationLabel', { + defaultMessage: 'Edit {lang} visualization', + values: { lang: language }, + })} + + + +

    +
    +
    + {navigateToLensEditor && ( + + + {i18n.translate('xpack.lens.config.editLinkLabel', { + defaultMessage: 'Edit in Lens', + })} + + + )} +
    +
    + )} + + * { + pointer-events: auto; + } + } + .euiFlyoutBody__overflowContent { + padding: 0; + block-size: 100%; + } + `} + > + {children} + + {isInlineFlyoutVisible && ( + + + + + + + + + + + + + + + )} + + ); +}; diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.test.tsx index 34adf0c9c2549..26428091032e9 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.test.tsx @@ -39,11 +39,11 @@ describe('Lens flyout', () => { newDatasourceState: 'newDatasourceState', }) ); - expect(updaterFn).toHaveBeenCalledWith('newDatasourceState', null); + expect(updaterFn).toHaveBeenCalledWith('newDatasourceState', null, 'testVis'); store.dispatch( updateVisualizationState({ visualizationId: 'testVis', newState: 'newVisState' }) ); - expect(updaterFn).toHaveBeenCalledWith('newDatasourceState', 'newVisState'); + expect(updaterFn).toHaveBeenCalledWith('newDatasourceState', 'newVisState', 'testVis'); }); test('updater is not run if it does not modify visualization or datasource state', () => { diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index fc6511b66ec15..91b094c141161 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { EuiFlyout, EuiLoadingSpinner, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Provider } from 'react-redux'; @@ -25,16 +25,21 @@ import { } from '../../../state_management'; import { generateId } from '../../../id_generator'; import type { DatasourceMap, VisualizationMap } from '../../../types'; -import { - LensEditConfigurationFlyout, - type EditConfigPanelProps, -} from './lens_configuration_flyout'; +import { LensEditConfigurationFlyout } from './lens_configuration_flyout'; +import type { EditConfigPanelProps } from './types'; import { SavedObjectIndexStore, type Document } from '../../../persistence'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; import { DOC_TYPE } from '../../../../common/constants'; export type EditLensConfigurationProps = Omit< EditConfigPanelProps, - 'startDependencies' | 'coreStart' | 'visualizationMap' | 'datasourceMap' | 'saveByRef' + | 'startDependencies' + | 'coreStart' + | 'visualizationMap' + | 'datasourceMap' + | 'saveByRef' + | 'setCurrentAttributes' + | 'previousAttributes' >; function LoadingSpinnerWithOverlay() { return ( @@ -44,7 +49,11 @@ function LoadingSpinnerWithOverlay() { ); } -type UpdaterType = (datasourceState: unknown, visualizationState: unknown) => void; +type UpdaterType = ( + datasourceState: unknown, + visualizationState: unknown, + visualizationType?: string +) => void; // exported for testing export const updatingMiddleware = @@ -68,7 +77,12 @@ export const updatingMiddleware = if (initExisting.match(action) || initEmpty.match(action)) { return; } - updater(datasourceStates[activeDatasourceId].state, visualization.state); + + updater( + datasourceStates[activeDatasourceId].state, + visualization.state, + visualization.activeId + ); } }; @@ -88,6 +102,7 @@ export async function getEditLensConfiguration( return ({ attributes, updatePanelState, + updateSuggestion, closeFlyout, wrapInFlyout, datasourceId, @@ -98,10 +113,13 @@ export async function getEditLensConfiguration( updateByRefInput, navigateToLensEditor, displayFlyoutHeader, + canEditTextBasedQuery, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap) { return ; } + const [currentAttributes, setCurrentAttributes] = + useState(attributes); /** * During inline editing of a by reference panel, the panel is converted to a by value one. * When the user applies the changes we save them to the Lens SO @@ -117,7 +135,7 @@ export async function getEditLensConfiguration( }, [savedObjectId] ); - const datasourceState = attributes.state.datasourceStates[datasourceId]; + const datasourceState = currentAttributes.state.datasourceStates[datasourceId]; const storeDeps = { lensServices, datasourceMap, @@ -135,7 +153,7 @@ export async function getEditLensConfiguration( lensStore.dispatch( loadInitial({ initialInput: { - attributes, + attributes: currentAttributes, id: panelId ?? generateId(), }, inlineEditing: true, @@ -148,6 +166,7 @@ export async function getEditLensConfiguration( { closeFlyout?.(); }} @@ -169,8 +188,9 @@ export async function getEditLensConfiguration( }; const configPanelProps = { - attributes, + attributes: currentAttributes, updatePanelState, + updateSuggestion, closeFlyout, datasourceId, coreStart, @@ -184,6 +204,8 @@ export async function getEditLensConfiguration( updateByRefInput, navigateToLensEditor, displayFlyoutHeader, + canEditTextBasedQuery, + setCurrentAttributes, }; return getWrapper( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.test.ts b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.test.ts new file mode 100644 index 0000000000000..0fe5f148a25d0 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.test.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import { createMockStartDependencies } from '../../../editor_frame_service/mocks'; +import { + mockVisualizationMap, + mockDatasourceMap, + mockDataViewWithTimefield, + mockAllSuggestions, +} from '../../../mocks'; +import { suggestionsApi } from '../../../lens_suggestions_api'; +import { fetchDataFromAggregateQuery } from '../../../datasources/text_based/fetch_data_from_aggregate_query'; +import { getSuggestions } from './helpers'; + +const mockSuggestionApi = suggestionsApi as jest.Mock; +const mockFetchData = fetchDataFromAggregateQuery as jest.Mock; + +jest.mock('../../../lens_suggestions_api', () => ({ + suggestionsApi: jest.fn(() => mockAllSuggestions), +})); + +jest.mock('../../../datasources/text_based/fetch_data_from_aggregate_query', () => ({ + fetchDataFromAggregateQuery: jest.fn(() => { + return { + columns: [ + { + name: '@timestamp', + id: '@timestamp', + meta: { + type: 'date', + }, + }, + { + name: 'bytes', + id: 'bytes', + meta: { + type: 'number', + }, + }, + { + name: 'memory', + id: 'memory', + meta: { + type: 'number', + }, + }, + ], + }; + }), +})); + +describe('getSuggestions', () => { + const query = { + esql: 'from index1 | limit 10 | stats average = avg(bytes', + }; + const mockStartDependencies = + createMockStartDependencies() as unknown as LensPluginStartDependencies; + const dataViews = dataViewPluginMocks.createStartContract(); + dataViews.create.mockResolvedValue(mockDataViewWithTimefield); + const dataviewSpecArr = [ + { + id: 'd2588ae7-9ea0-4439-9f5b-f808754a3b97', + title: 'index1', + timeFieldName: '@timestamp', + sourceFilters: [], + fieldFormats: {}, + runtimeFieldMap: {}, + fieldAttrs: {}, + allowNoIndex: false, + name: 'index1', + }, + ]; + const startDependencies = { + ...mockStartDependencies, + dataViews, + }; + + it('returns the suggestions attributes correctly', async () => { + const suggestionsAttributes = await getSuggestions( + query, + startDependencies, + mockDatasourceMap(), + mockVisualizationMap(), + dataviewSpecArr, + jest.fn() + ); + expect(suggestionsAttributes?.visualizationType).toBe(mockAllSuggestions[0].visualizationId); + expect(suggestionsAttributes?.state.visualization).toStrictEqual( + mockAllSuggestions[0].visualizationState + ); + }); + + it('returns undefined if no suggestions are computed', async () => { + mockSuggestionApi.mockResolvedValueOnce([]); + const suggestionsAttributes = await getSuggestions( + query, + startDependencies, + mockDatasourceMap(), + mockVisualizationMap(), + dataviewSpecArr, + jest.fn() + ); + expect(suggestionsAttributes).toBeUndefined(); + }); + + it('returns an error if fetching the data fails', async () => { + mockFetchData.mockImplementation(() => { + throw new Error('sorry!'); + }); + const setErrorsSpy = jest.fn(); + const suggestionsAttributes = await getSuggestions( + query, + startDependencies, + mockDatasourceMap(), + mockVisualizationMap(), + dataviewSpecArr, + setErrorsSpy + ); + expect(suggestionsAttributes).toBeUndefined(); + expect(setErrorsSpy).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts new file mode 100644 index 0000000000000..faecb37ba7fd7 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/helpers.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { getIndexPatternFromSQLQuery, getIndexPatternFromESQLQuery } from '@kbn/es-query'; +import type { AggregateQuery, Query, Filter } from '@kbn/es-query'; +import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public'; +import type { Suggestion } from '../../../types'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import type { DatasourceMap, VisualizationMap } from '../../../types'; +import { fetchDataFromAggregateQuery } from '../../../datasources/text_based/fetch_data_from_aggregate_query'; +import { suggestionsApi } from '../../../lens_suggestions_api'; + +export const getQueryColumns = async ( + query: AggregateQuery, + dataView: DataView, + deps: LensPluginStartDependencies +) => { + // Fetching only columns for ES|QL for performance reasons with limit 0 + // Important note: ES doesnt return the warnings for 0 limit, + // I am skipping them in favor of performance now + // but we should think another way to get them (from Lens embeddable or store) + const performantQuery = { ...query }; + if ('esql' in performantQuery && performantQuery.esql) { + performantQuery.esql = `${performantQuery.esql} | limit 0`; + } + const table = await fetchDataFromAggregateQuery( + performantQuery, + dataView, + deps.data, + deps.expressions + ); + return table?.columns; +}; + +export const getSuggestions = async ( + query: AggregateQuery, + deps: LensPluginStartDependencies, + datasourceMap: DatasourceMap, + visualizationMap: VisualizationMap, + adHocDataViews: DataViewSpec[], + setErrors: (errors: Error[]) => void +) => { + try { + let indexPattern = ''; + if ('sql' in query) { + indexPattern = getIndexPatternFromSQLQuery(query.sql); + } + if ('esql' in query) { + indexPattern = getIndexPatternFromESQLQuery(query.esql); + } + const dataViewSpec = adHocDataViews.find((adHoc) => { + return adHoc.name === indexPattern; + }); + + const dataView = await deps.dataViews.create( + dataViewSpec ?? { + title: indexPattern, + } + ); + if (dataView.fields.getByName('@timestamp')?.type === 'date' && !dataViewSpec) { + dataView.timeFieldName = '@timestamp'; + } + const columns = await getQueryColumns(query, dataView, deps); + const context = { + dataViewSpec: dataView?.toSpec(), + fieldName: '', + textBasedColumns: columns, + query, + }; + + const allSuggestions = + suggestionsApi({ context, dataView, datasourceMap, visualizationMap }) ?? []; + + // Lens might not return suggestions for some cases, i.e. in case of errors + if (!allSuggestions.length) return undefined; + + const firstSuggestion = allSuggestions[0]; + + const attrs = getLensAttributes({ + filters: [], + query, + suggestion: firstSuggestion, + dataView, + }); + return attrs; + } catch (e) { + setErrors([e]); + } + return undefined; +}; + +export const getLensAttributes = ({ + filters, + query, + suggestion, + dataView, +}: { + filters: Filter[]; + query: Query | AggregateQuery; + suggestion: Suggestion | undefined; + dataView?: DataView; +}) => { + const suggestionDatasourceState = Object.assign({}, suggestion?.datasourceState); + const suggestionVisualizationState = Object.assign({}, suggestion?.visualizationState); + const datasourceStates = + suggestion && suggestion.datasourceState + ? { + [suggestion.datasourceId!]: { + ...suggestionDatasourceState, + }, + } + : { + formBased: {}, + }; + const visualization = suggestionVisualizationState; + const attributes = { + title: suggestion + ? suggestion.title + : i18n.translate('xpack.lens.config.suggestion.title', { + defaultMessage: 'New suggestion', + }), + references: [ + { + id: dataView?.id ?? '', + name: `textBasedLanguages-datasource-layer-suggestion`, + type: 'index-pattern', + }, + ], + state: { + datasourceStates, + filters, + query, + visualization, + ...(dataView && + dataView.id && + !dataView.isPersisted() && { + adHocDataViews: { [dataView.id]: dataView.toSpec(false) }, + }), + }, + visualizationType: suggestion ? suggestion.visualizationId : 'lnsXY', + } as TypedLensByValueInput['attributes']; + return attributes; +}; diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/layer_configuration_section.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/layer_configuration_section.tsx new file mode 100644 index 0000000000000..7ae7b456ab669 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/layer_configuration_section.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiSpacer, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; +import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; +import { createIndexPatternService } from '../../../data_views_service/service'; +import { useLensDispatch, updateIndexPatterns } from '../../../state_management'; +import { replaceIndexpattern } from '../../../state_management/lens_slice'; +import type { LayerConfigurationProps } from './types'; +import { useLensSelector } from '../../../state_management'; + +export function LayerConfiguration({ + attributes, + coreStart, + startDependencies, + visualizationMap, + datasourceMap, + datasourceId, + framePublicAPI, + hasPadding, + setIsInlineFlyoutVisible, +}: LayerConfigurationProps) { + const dispatch = useLensDispatch(); + const { euiTheme } = useEuiTheme(); + const { visualization } = useLensSelector((state) => state.lens); + const activeVisualization = + visualizationMap[visualization.activeId ?? attributes.visualizationType]; + const indexPatternService = useMemo( + () => + createIndexPatternService({ + dataViews: startDependencies.dataViews, + uiActions: startDependencies.uiActions, + core: coreStart, + updateIndexPatterns: (newIndexPatternsState, options) => { + dispatch(updateIndexPatterns(newIndexPatternsState)); + }, + replaceIndexPattern: (newIndexPattern, oldId, options) => { + dispatch(replaceIndexpattern({ newIndexPattern, oldId })); + }, + }), + [coreStart, dispatch, startDependencies.dataViews, startDependencies.uiActions] + ); + + const layerPanelsProps = { + framePublicAPI, + datasourceMap, + visualizationMap, + core: coreStart, + dataViews: startDependencies.dataViews, + uiActions: startDependencies.uiActions, + hideLayerHeader: datasourceId === 'textBased', + indexPatternService, + setIsInlineFlyoutVisible, + }; + return ( +
    + + + +
    + ); +} diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index 4731afe037249..0462e4ad251de 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -14,10 +14,8 @@ import { mockVisualizationMap, mockDatasourceMap, mockDataPlugin } from '../../. import type { LensPluginStartDependencies } from '../../../plugin'; import { createMockStartDependencies } from '../../../editor_frame_service/mocks'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; -import { - LensEditConfigurationFlyout, - type EditConfigPanelProps, -} from './lens_configuration_flyout'; +import { LensEditConfigurationFlyout } from './lens_configuration_flyout'; +import type { EditConfigPanelProps } from './types'; const lensAttributes = { title: 'test', @@ -29,14 +27,12 @@ const lensAttributes = { visualization: {}, filters: [], query: { - language: 'lucene', - query: '', + esql: 'from index1 | limit 10', }, }, filters: [], query: { - language: 'lucene', - query: '', + esql: 'from index1 | limit 10', }, references: [], } as unknown as TypedLensByValueInput['attributes']; @@ -109,6 +105,16 @@ describe('LensEditConfigurationFlyout', () => { expect(closeFlyoutSpy).toHaveBeenCalled(); }); + it('should call the updatePanelState callback if cancel button is clicked', async () => { + const updatePanelStateSpy = jest.fn(); + renderConfigFlyout({ + updatePanelState: updatePanelStateSpy, + }); + expect(screen.getByTestId('lns-layerPanel-0')).toBeInTheDocument(); + userEvent.click(screen.getByTestId('cancelFlyoutButton')); + expect(updatePanelStateSpy).toHaveBeenCalled(); + }); + it('should call the updateByRefInput callback if cancel button is clicked and savedObjectId exists', async () => { const updateByRefInputSpy = jest.fn(); @@ -135,4 +141,41 @@ describe('LensEditConfigurationFlyout', () => { expect(updateByRefInputSpy).toHaveBeenCalled(); expect(saveByRefSpy).toHaveBeenCalled(); }); + + it('should not display the editor if canEditTextBasedQuery prop is false', async () => { + renderConfigFlyout({ + canEditTextBasedQuery: false, + }); + expect(screen.queryByTestId('TextBasedLangEditor')).toBeNull(); + }); + + it('should not display the editor if canEditTextBasedQuery prop is true but the query is not text based', async () => { + renderConfigFlyout({ + canEditTextBasedQuery: true, + attributes: { + ...lensAttributes, + state: { + ...lensAttributes.state, + query: { + type: 'kql', + query: '', + } as unknown as Query, + }, + }, + }); + expect(screen.queryByTestId('TextBasedLangEditor')).toBeNull(); + }); + + it('should display the suggestions if canEditTextBasedQuery prop is true', async () => { + renderConfigFlyout( + { + canEditTextBasedQuery: true, + }, + { + esql: 'from index1 | limit 10', + } + ); + expect(screen.getByTestId('InlineEditingESQLEditor')).toBeInTheDocument(); + expect(screen.getByTestId('InlineEditingSuggestions')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index a60e3df063aaa..72dbd313d6452 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -6,85 +6,34 @@ */ import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react'; +import { isEqual } from 'lodash'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; import { - EuiButtonEmpty, - EuiButton, - EuiFlyoutBody, - EuiFlyoutFooter, EuiTitle, - EuiLink, - EuiIcon, - EuiToolTip, - EuiSpacer, + EuiAccordion, + useEuiTheme, EuiFlexGroup, EuiFlexItem, - useEuiTheme, - EuiCallOut, + euiScrollBarStyles, } from '@elastic/eui'; -import { isEqual } from 'lodash'; -import type { Observable } from 'rxjs'; import { euiThemeVars } from '@kbn/ui-theme'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { css } from '@emotion/react'; -import type { CoreStart } from '@kbn/core/public'; import type { Datatable } from '@kbn/expressions-plugin/public'; -import type { LensPluginStartDependencies } from '../../../plugin'; import { - useLensSelector, - selectFramePublicAPI, - useLensDispatch, - updateIndexPatterns, -} from '../../../state_management'; -import { replaceIndexpattern } from '../../../state_management/lens_slice'; -import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; - -import type { DatasourceMap, VisualizationMap } from '../../../types'; + getAggregateQueryMode, + isOfAggregateQueryType, + getLanguageDisplayName, +} from '@kbn/es-query'; +import type { AggregateQuery, Query } from '@kbn/es-query'; +import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { useLensSelector, selectFramePublicAPI } from '../../../state_management'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; -import type { LensEmbeddableOutput } from '../../../embeddable'; -import type { LensInspector } from '../../../lens_inspector_service'; -import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; import { extractReferencesFromState } from '../../../utils'; -import type { Document } from '../../../persistence'; -import { createIndexPatternService } from '../../../data_views_service/service'; - -export interface EditConfigPanelProps { - coreStart: CoreStart; - startDependencies: LensPluginStartDependencies; - visualizationMap: VisualizationMap; - datasourceMap: DatasourceMap; - /** The attributes of the Lens embeddable */ - attributes: TypedLensByValueInput['attributes']; - /** Callback for updating the visualization and datasources state */ - updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; - /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ - datasourceId: 'formBased' | 'textBased'; - /** Embeddable output observable, useful for dashboard flyout */ - output$?: Observable; - /** Contains the active data, necessary for some panel configuration such as coloring */ - lensAdapters?: LensInspector['adapters']; - /** Optional callback called when updating the by reference embeddable */ - updateByRefInput?: (soId: string) => void; - /** Callback for closing the edit flyout */ - closeFlyout?: () => void; - /** Boolean used for adding a flyout wrapper */ - wrapInFlyout?: boolean; - /** Optional parameter for panel identification - * If not given, Lens generates a new one - */ - panelId?: string; - /** Optional parameter for saved object id - * Should be given if the lens embeddable is a by reference one - * (saved in the library) - */ - savedObjectId?: string; - /** Callback for saving the embeddable as a SO */ - saveByRef?: (attrs: Document) => void; - /** Optional callback for navigation from the header of the flyout */ - navigateToLensEditor?: () => void; - /** If set to true it displays a header on the flyout */ - displayFlyoutHeader?: boolean; -} +import { LayerConfiguration } from './layer_configuration_section'; +import type { EditConfigPanelProps } from './types'; +import { FlyoutWrapper } from './flyout_wrapper'; +import { getSuggestions } from './helpers'; +import { SuggestionPanel } from '../../../editor_frame_service/editor_frame/suggestion_panel'; export function LensEditConfigurationFlyout({ attributes, @@ -94,6 +43,8 @@ export function LensEditConfigurationFlyout({ datasourceMap, datasourceId, updatePanelState, + updateSuggestion, + setCurrentAttributes, closeFlyout, saveByRef, savedObjectId, @@ -102,15 +53,21 @@ export function LensEditConfigurationFlyout({ lensAdapters, navigateToLensEditor, displayFlyoutHeader, + canEditTextBasedQuery, }: EditConfigPanelProps) { + const euiTheme = useEuiTheme(); const previousAttributes = useRef(attributes); + const prevQuery = useRef(attributes.state.query); + const [query, setQuery] = useState(attributes.state.query); + const [errors, setErrors] = useState(); + const [isInlineFlyoutVisible, setIsInlineFlyoutVisible] = useState(true); + const [isLayerAccordionOpen, setIsLayerAccordionOpen] = useState(true); + const [isSuggestionsAccordionOpen, setIsSuggestionsAccordionOpen] = useState(false); const datasourceState = attributes.state.datasourceStates[datasourceId]; const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; - const [isInlineFooterVisible, setIsInlineFlyoutFooterVisible] = useState(true); - const { euiTheme } = useEuiTheme(); const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); - const dispatch = useLensDispatch(); + const suggestsLimitedColumns = activeDatasource?.suggestsLimitedColumns?.(datasourceState); const activeData: Record = useMemo(() => { return {}; }, []); @@ -149,28 +106,34 @@ export function LensEditConfigurationFlyout({ const onCancel = useCallback(() => { const previousAttrs = previousAttributes.current; - if (attributesChanged) { - const currentDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers - ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( - previousAttrs.state.datasourceStates[datasourceId], - previousAttrs.references - ) - : previousAttrs.state.datasourceStates[datasourceId]; - updatePanelState?.(currentDatasourceState, previousAttrs.state.visualization); + if (previousAttrs.visualizationType === visualization.activeId) { + const currentDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers + ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( + previousAttrs.state.datasourceStates[datasourceId], + previousAttrs.references + ) + : previousAttrs.state.datasourceStates[datasourceId]; + updatePanelState?.(currentDatasourceState, previousAttrs.state.visualization); + } else { + updateSuggestion?.(previousAttrs); + } if (savedObjectId) { updateByRefInput?.(savedObjectId); } } closeFlyout?.(); }, [ + previousAttributes, attributesChanged, - savedObjectId, closeFlyout, datasourceMap, datasourceId, updatePanelState, + updateSuggestion, + savedObjectId, updateByRefInput, + visualization, ]); const onApply = useCallback(() => { @@ -218,20 +181,33 @@ export function LensEditConfigurationFlyout({ datasourceMap, ]); - const indexPatternService = useMemo( - () => - createIndexPatternService({ - dataViews: startDependencies.dataViews, - uiActions: startDependencies.uiActions, - core: coreStart, - updateIndexPatterns: (newIndexPatternsState, options) => { - dispatch(updateIndexPatterns(newIndexPatternsState)); - }, - replaceIndexPattern: (newIndexPattern, oldId, options) => { - dispatch(replaceIndexpattern({ newIndexPattern, oldId })); - }, - }), - [coreStart, dispatch, startDependencies.dataViews, startDependencies.uiActions] + // needed for text based languages mode which works ONLY with adHoc dataviews + const adHocDataViews = Object.values(attributes.state.adHocDataViews ?? {}); + + const runQuery = useCallback( + async (q) => { + const attrs = await getSuggestions( + q, + startDependencies, + datasourceMap, + visualizationMap, + adHocDataViews, + setErrors + ); + if (attrs) { + setCurrentAttributes?.(attrs); + setErrors([]); + updateSuggestion?.(attrs); + } + }, + [ + startDependencies, + datasourceMap, + visualizationMap, + adHocDataViews, + setCurrentAttributes, + updateSuggestion, + ] ); const framePublicAPI = useLensSelector((state) => { @@ -244,155 +220,197 @@ export function LensEditConfigurationFlyout({ }; return selectFramePublicAPI(newState, datasourceMap); }); + + const textBasedMode = isOfAggregateQueryType(query) ? getAggregateQueryMode(query) : undefined; + if (isLoading) return null; + // Example is the Discover editing where we dont want to render the text based editor on the panel + if (!canEditTextBasedQuery) { + return ( + + + + ); + } - const layerPanelsProps = { - framePublicAPI, - datasourceMap, - visualizationMap, - core: coreStart, - dataViews: startDependencies.dataViews, - uiActions: startDependencies.uiActions, - hideLayerHeader: datasourceId === 'textBased', - indexPatternService, - setIsInlineFlyoutFooterVisible, - }; return ( <> - * { - pointer-events: auto; - } - } - .euiFlyoutBody__overflowContent { - padding: 0; - } - `} + - - {displayFlyoutHeader && ( - - - - - - -

    - {i18n.translate('xpack.lens.config.editVisualizationLabel', { - defaultMessage: 'Edit visualization', - })} -

    -
    -
    - - - - - -
    -
    - {navigateToLensEditor && ( - - - {i18n.translate('xpack.lens.config.editLinkLabel', { - defaultMessage: 'Edit in Lens', - })} - - - )} -
    + + {isOfAggregateQueryType(query) && ( + + { + setQuery(q); + prevQuery.current = q; + }} + expandCodeEditor={(status: boolean) => {}} + isCodeEditorExpanded + detectTimestamp={Boolean(adHocDataViews?.[0]?.timeFieldName)} + errors={errors} + warning={ + suggestsLimitedColumns + ? i18n.translate('xpack.lens.config.configFlyoutCallout', { + defaultMessage: + 'Displaying a limited portion of the available fields. Add more from the configuration panel.', + }) + : undefined + } + hideMinimizeButton + editorIsInline + hideRunQueryText + disableSubmitAction={isEqual(query, prevQuery.current)} + onTextLangQuerySubmit={(q) => { + if (q) { + runQuery(q); + } + }} + isDisabled={false} + /> )} - {datasourceId === 'textBased' && ( - +
    + {i18n.translate('xpack.lens.config.layerConfigurationLabel', { + defaultMessage: 'Layer configuration', + })} +
    + + } + buttonProps={{ + paddingSize: 'm', + }} + initialIsOpen={isLayerAccordionOpen} + forceState={isLayerAccordionOpen ? 'open' : 'closed'} + onToggle={(status) => { + if (status && isSuggestionsAccordionOpen) { + setIsSuggestionsAccordionOpen(!status); + } + setIsLayerAccordionOpen(!isLayerAccordionOpen); + }} + > + - )} - - - - +
    + + + { + if (!status && isLayerAccordionOpen) { + setIsLayerAccordionOpen(status); + } + setIsSuggestionsAccordionOpen(!isSuggestionsAccordionOpen); + }} />
    -
    - {isInlineFooterVisible && ( - - - - - - - - - - - - - - - )} + ); } diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/types.ts b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/types.ts new file mode 100644 index 0000000000000..ae207865bbf9a --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/types.ts @@ -0,0 +1,85 @@ +/* + * Copyright 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 { Observable } from 'rxjs'; +import type { CoreStart } from '@kbn/core/public'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import type { DatasourceMap, VisualizationMap, FramePublicAPI } from '../../../types'; +import type { LensEmbeddableOutput } from '../../../embeddable'; +import type { LensInspector } from '../../../lens_inspector_service'; +import type { Document } from '../../../persistence'; + +export interface FlyoutWrapperProps { + children: JSX.Element; + isInlineFlyoutVisible: boolean; + isScrollable: boolean; + displayFlyoutHeader?: boolean; + language?: string; + attributesChanged?: boolean; + onCancel?: () => void; + onApply?: () => void; + navigateToLensEditor?: () => void; +} + +export interface EditConfigPanelProps { + coreStart: CoreStart; + startDependencies: LensPluginStartDependencies; + visualizationMap: VisualizationMap; + datasourceMap: DatasourceMap; + /** The attributes of the Lens embeddable */ + attributes: TypedLensByValueInput['attributes']; + /** Callback for updating the visualization and datasources state.*/ + updatePanelState: ( + datasourceState: unknown, + visualizationState: unknown, + visualizationType?: string + ) => void; + updateSuggestion?: (attrs: TypedLensByValueInput['attributes']) => void; + /** Set the attributes state */ + setCurrentAttributes?: (attrs: TypedLensByValueInput['attributes']) => void; + /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ + datasourceId: 'formBased' | 'textBased'; + /** Embeddable output observable, useful for dashboard flyout */ + output$?: Observable; + /** Contains the active data, necessary for some panel configuration such as coloring */ + lensAdapters?: LensInspector['adapters']; + /** Optional callback called when updating the by reference embeddable */ + updateByRefInput?: (soId: string) => void; + /** Callback for closing the edit flyout */ + closeFlyout?: () => void; + /** Boolean used for adding a flyout wrapper */ + wrapInFlyout?: boolean; + /** Optional parameter for panel identification + * If not given, Lens generates a new one + */ + panelId?: string; + /** Optional parameter for saved object id + * Should be given if the lens embeddable is a by reference one + * (saved in the library) + */ + savedObjectId?: string; + /** Callback for saving the embeddable as a SO */ + saveByRef?: (attrs: Document) => void; + /** Optional callback for navigation from the header of the flyout */ + navigateToLensEditor?: () => void; + /** If set to true it displays a header on the flyout */ + displayFlyoutHeader?: boolean; + /** If set to true the layout changes to accordion and the text based query (i.e. ES|QL) can be edited */ + canEditTextBasedQuery?: boolean; +} + +export interface LayerConfigurationProps { + attributes: TypedLensByValueInput['attributes']; + coreStart: CoreStart; + startDependencies: LensPluginStartDependencies; + visualizationMap: VisualizationMap; + datasourceMap: DatasourceMap; + datasourceId: 'formBased' | 'textBased'; + framePublicAPI: FramePublicAPI; + hasPadding?: boolean; + setIsInlineFlyoutVisible: (flag: boolean) => void; +} diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts index baa176a1cb514..2213391235847 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/editor/math_completion.ts @@ -215,18 +215,18 @@ export function getPossibleFunctions( operationDefinitionMap?: Record ) { const available = memoizedGetAvailableOperationsByMetadata(indexPattern, operationDefinitionMap); - const possibleOperationNames: string[] = []; - available.forEach((a) => { + const possibleOperationNames: Set = new Set(); + for (const a of available) { if (a.operationMetaData.dataType === 'number' && !a.operationMetaData.isBucketed) { - possibleOperationNames.push( - ...a.operations - .filter((o) => o.type !== 'managedReference' || o.usedInMath) - .map((o) => o.operationType) - ); + for (const o of a.operations) { + if (o.type !== 'managedReference' || o.usedInMath) { + possibleOperationNames.add(o.operationType); + } + } } - }); + } - return [...uniq(possibleOperationNames), ...Object.keys(tinymathFunctions)]; + return Array.from(possibleOperationNames.keys()).concat(Object.keys(tinymathFunctions)); } function getFunctionSuggestions( @@ -261,7 +261,7 @@ function getArgumentSuggestions( if (tinymathFunction) { if (tinymathFunction.positionalArguments[position]) { return { - list: uniq(getPossibleFunctions(indexPattern, operationDefinitionMap)).map((f) => ({ + list: getPossibleFunctions(indexPattern, operationDefinitionMap).map((f) => ({ type: 'math' as const, label: f, })), diff --git a/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.test.tsx b/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.test.tsx index ef4811f254cb2..ca9d48c17cbb8 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.test.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.test.tsx @@ -13,6 +13,7 @@ import { column3, numericDraggedColumn, fieldList, + fieldListNonNumericOnly, notNumericDraggedField, numericDraggedField, } from './mocks'; @@ -74,6 +75,23 @@ describe('Text-based: getDropProps', () => { } as unknown as DatasourceDimensionDropHandlerProps; expect(getDropProps(props)).toBeUndefined(); }); + it('should not return undefined if source is a non-numeric field, target is a metric dimension but datatable doesnt have numeric fields', () => { + const props = { + ...defaultProps, + state: { + ...defaultProps.state, + layers: { + first: { + columns: [column1, column2, column3], + allColumns: [...fieldListNonNumericOnly, column1, column2, column3], + }, + }, + fieldList: fieldListNonNumericOnly, + }, + source: notNumericDraggedField, + } as unknown as DatasourceDimensionDropHandlerProps; + expect(getDropProps(props)).toEqual({ dropTypes: ['field_replace'], nextLabel: 'category' }); + }); it('should return reorder if source and target are operations from the same group', () => { const props = { ...defaultProps, diff --git a/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.tsx b/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.tsx index 9f79fff3d6080..78e1c98f3a301 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/dnd/get_drop_props.tsx @@ -10,6 +10,7 @@ import { isOperation } from '../../../types'; import type { TextBasedPrivateState } from '../types'; import type { GetDropPropsArgs } from '../../../types'; import { isDraggedField, isOperationFromTheSameGroup } from '../../../utils'; +import { canColumnBeDroppedInMetricDimension } from '../utils'; export const getDropProps = ( props: GetDropPropsArgs @@ -44,14 +45,24 @@ export const getDropProps = ( return { dropTypes: ['reorder'], nextLabel }; } + const sourceFieldCanMoveToMetricDimension = canColumnBeDroppedInMetricDimension( + layer.allColumns, + sourceField?.meta?.type + ); + + const targetFieldCanMoveToMetricDimension = canColumnBeDroppedInMetricDimension( + layer.allColumns, + targetField?.meta?.type + ); + const isMoveable = !target?.isMetricDimension || - (target.isMetricDimension && sourceField?.meta?.type === 'number'); + (target.isMetricDimension && sourceFieldCanMoveToMetricDimension); if (targetColumn) { const isSwappable = (isMoveable && !source?.isMetricDimension) || - (source.isMetricDimension && targetField?.meta?.type === 'number'); + (source.isMetricDimension && targetFieldCanMoveToMetricDimension); if (isMoveable) { if (isSwappable) { return { diff --git a/x-pack/plugins/lens/public/datasources/text_based/dnd/mocks.tsx b/x-pack/plugins/lens/public/datasources/text_based/dnd/mocks.tsx index 5cb3bcd37e2cc..90a37acab1043 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/dnd/mocks.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/dnd/mocks.tsx @@ -83,6 +83,37 @@ export const numericDraggedField = { }, }; +export const fieldListNonNumericOnly = [ + { + columnId: 'category', + fieldName: 'category', + meta: { + type: 'string', + }, + }, + { + columnId: 'currency', + fieldName: 'currency', + meta: { + type: 'string', + }, + }, + { + columnId: 'products.sold_date', + fieldName: 'products.sold_date', + meta: { + type: 'date', + }, + }, + { + columnId: 'products.buyer', + fieldName: 'products.buyer', + meta: { + type: 'string', + }, + }, +]; + export const fieldList = [ { columnId: 'category', diff --git a/x-pack/plugins/lens/public/datasources/text_based/layerpanel.test.tsx b/x-pack/plugins/lens/public/datasources/text_based/layerpanel.test.tsx deleted file mode 100644 index d6e4be8c99387..0000000000000 --- a/x-pack/plugins/lens/public/datasources/text_based/layerpanel.test.tsx +++ /dev/null @@ -1,86 +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 React from 'react'; -import type { DatatableColumn } from '@kbn/expressions-plugin/public'; -import { TextBasedPrivateState } from './types'; -import type { DataViewsState } from '../../state_management/types'; - -import { TextBasedLayerPanelProps, LayerPanel } from './layerpanel'; -import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; -import { ChangeIndexPattern } from '../../shared_components/dataview_picker/dataview_picker'; - -const fields = [ - { - name: 'timestamp', - id: 'timestamp', - meta: { - type: 'date', - }, - }, - { - name: 'bytes', - id: 'bytes', - meta: { - type: 'number', - }, - }, - { - name: 'memory', - id: 'memory', - meta: { - type: 'number', - }, - }, -] as DatatableColumn[]; - -const initialState: TextBasedPrivateState = { - layers: { - first: { - index: '1', - columns: [], - allColumns: [], - query: { sql: 'SELECT * FROM foo' }, - }, - }, - indexPatternRefs: [ - { id: '1', title: 'my-fake-index-pattern' }, - { id: '2', title: 'my-fake-restricted-pattern' }, - { id: '3', title: 'my-compatible-pattern' }, - ], - fieldList: fields, -}; -describe('Layer Data Panel', () => { - let defaultProps: TextBasedLayerPanelProps; - - beforeEach(() => { - defaultProps = { - layerId: 'first', - state: initialState, - onChangeIndexPattern: jest.fn(), - dataViews: { - indexPatternRefs: [ - { id: '1', title: 'my-fake-index-pattern', name: 'My fake index pattern' }, - { id: '2', title: 'my-fake-restricted-pattern', name: 'my-fake-restricted-pattern' }, - { id: '3', title: 'my-compatible-pattern', name: 'my-compatible-pattern' }, - ], - indexPatterns: {}, - } as DataViewsState, - }; - }); - - it('should display the selected dataview but disabled', () => { - const instance = shallow(); - expect(instance.find(ChangeIndexPattern).prop('trigger')).toStrictEqual({ - fontWeight: 'normal', - isDisabled: true, - label: 'my-fake-index-pattern', - size: 's', - title: 'my-fake-index-pattern', - }); - }); -}); diff --git a/x-pack/plugins/lens/public/datasources/text_based/layerpanel.tsx b/x-pack/plugins/lens/public/datasources/text_based/layerpanel.tsx deleted file mode 100644 index 3ce8c333114ff..0000000000000 --- a/x-pack/plugins/lens/public/datasources/text_based/layerpanel.tsx +++ /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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { DatasourceLayerPanelProps } from '../../types'; -import { TextBasedPrivateState } from './types'; -import { ChangeIndexPattern } from '../../shared_components/dataview_picker/dataview_picker'; - -export interface TextBasedLayerPanelProps extends DatasourceLayerPanelProps { - state: TextBasedPrivateState; -} - -export function LayerPanel({ state, layerId, dataViews }: TextBasedLayerPanelProps) { - const layer = state.layers[layerId]; - const dataView = state.indexPatternRefs.find((ref) => ref.id === layer.index); - - const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingDataView', { - defaultMessage: 'Data view not found', - }); - return ( - {}} - /> - ); -} diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts index 8aab2f3670959..604e45f72ec84 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts @@ -116,7 +116,7 @@ describe('Textbased Data Source', () => { }, ], index: 'foo', - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, }, }, fieldList: [ @@ -226,7 +226,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: 'foo', }, }, @@ -261,7 +261,7 @@ describe('Textbased Data Source', () => { ...baseState.layers, newLayer: { index: 'foo', - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, allColumns: [ { columnId: 'col1', @@ -296,7 +296,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: 'foo', }, }, @@ -353,7 +353,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: 'foo', }, }, @@ -401,13 +401,22 @@ describe('Textbased Data Source', () => { ); expect(suggestions[0].state).toEqual({ ...state, + initialContext: undefined, fieldList: textBasedQueryColumns, + indexPatternRefs: [ + { + id: '1', + timeField: undefined, + title: 'foo', + }, + ], layers: { newid: { allColumns: [ { columnId: 'bytes', fieldName: 'bytes', + inMetricDimension: true, meta: { type: 'number', }, @@ -424,6 +433,7 @@ describe('Textbased Data Source', () => { { columnId: 'bytes', fieldName: 'bytes', + inMetricDimension: true, meta: { type: 'number', }, @@ -466,6 +476,7 @@ describe('Textbased Data Source', () => { ], isMultiRow: false, layerId: 'newid', + notAssignedMetrics: false, }); }); @@ -504,6 +515,176 @@ describe('Textbased Data Source', () => { ); expect(suggestions).toEqual([]); }); + + it('should return the correct suggestions if non numeric columns are given', () => { + const textBasedQueryColumns = [ + { + id: '@timestamp', + name: '@timestamp', + meta: { + type: 'date', + }, + }, + { + id: 'dest', + name: 'dest', + meta: { + type: 'string', + }, + }, + ]; + const state = { + layers: {}, + initialContext: { + textBasedColumns: textBasedQueryColumns, + query: { esql: 'from foo' }, + dataViewSpec: { + title: 'foo', + id: '1', + name: 'Foo', + }, + }, + } as unknown as TextBasedPrivateState; + const suggestions = TextBasedDatasource.getDatasourceSuggestionsForVisualizeField( + state, + '1', + '', + indexPatterns + ); + expect(suggestions[0].state).toEqual({ + ...state, + initialContext: undefined, + fieldList: textBasedQueryColumns, + indexPatternRefs: [ + { + id: '1', + timeField: undefined, + title: 'foo', + }, + ], + layers: { + newid: { + allColumns: [ + { + columnId: '@timestamp', + fieldName: '@timestamp', + inMetricDimension: true, + meta: { + type: 'date', + }, + }, + { + columnId: 'dest', + fieldName: 'dest', + inMetricDimension: true, + meta: { + type: 'string', + }, + }, + ], + columns: [ + { + columnId: '@timestamp', + fieldName: '@timestamp', + inMetricDimension: true, + meta: { + type: 'date', + }, + }, + { + columnId: 'dest', + fieldName: 'dest', + inMetricDimension: true, + meta: { + type: 'string', + }, + }, + ], + index: '1', + query: { + esql: 'from foo', + }, + }, + }, + }); + + expect(suggestions[0].table).toEqual({ + changeType: 'initial', + columns: [ + { + columnId: '@timestamp', + operation: { + dataType: 'date', + isBucketed: true, + label: '@timestamp', + }, + }, + { + columnId: 'dest', + operation: { + dataType: 'string', + isBucketed: true, + label: 'dest', + }, + }, + ], + isMultiRow: false, + layerId: 'newid', + notAssignedMetrics: true, + }); + }); + }); + + describe('#suggestsLimitedColumns', () => { + it('should return true if query returns big number of columns', () => { + const fieldList = [ + { + id: 'a', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + { + id: 'b', + name: 'Test 2', + meta: { + type: 'number', + }, + }, + { + id: 'c', + name: 'Test 3', + meta: { + type: 'date', + }, + }, + { + id: 'd', + name: 'Test 4', + meta: { + type: 'string', + }, + }, + { + id: 'e', + name: 'Test 5', + meta: { + type: 'string', + }, + }, + ]; + const state = { + fieldList, + layers: { + a: { + query: { esql: 'from foo' }, + index: 'foo', + }, + }, + } as unknown as TextBasedPrivateState; + expect(TextBasedDatasource?.suggestsLimitedColumns?.(state)).toBeTruthy(); + }); }); describe('#getUserMessages', () => { @@ -544,7 +725,7 @@ describe('Textbased Data Source', () => { }, ], errors: [new Error('error 1'), new Error('error 2')], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: 'foo', }, }, @@ -626,7 +807,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: '1', }, }, @@ -673,7 +854,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: '1', }, }, @@ -731,7 +912,7 @@ describe('Textbased Data Source', () => { }, }, ], - query: { sql: 'SELECT * FROM foo' }, + query: { esql: 'FROM foo' }, index: '1', }, }, @@ -759,11 +940,14 @@ describe('Textbased Data Source', () => { }, Object { "arguments": Object { + "locale": Array [ + "en", + ], "query": Array [ - "SELECT * FROM foo", + "FROM foo", ], }, - "function": "essql", + "function": "esql", "type": "function", }, Object { diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index 43d971caf24a9..bc46f0b4076d9 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -42,10 +42,10 @@ import type { } from './types'; import { FieldSelect } from './field_select'; import type { Datasource } from '../../types'; -import { LayerPanel } from './layerpanel'; import { getUniqueLabelGenerator, nonNullable } from '../../utils'; import { onDrop, getDropProps } from './dnd'; import { removeColumn } from './remove_column'; +import { canColumnBeUsedBeInMetricDimension, MAX_NUM_OF_COLUMNS } from './utils'; function getLayerReferenceName(layerId: string) { return `textBasedLanguages-datasource-layer-${layerId}`; @@ -88,12 +88,20 @@ export function getTextBasedDatasource({ layerId: id, columns: layer.columns?.map((f) => { + const inMetricDimension = canColumnBeUsedBeInMetricDimension( + layer.allColumns, + f?.meta?.type + ); return { columnId: f.columnId, operation: { dataType: f?.meta?.type as DataType, label: f.fieldName, isBucketed: Boolean(f?.meta?.type !== 'number'), + // makes non-number fields to act as metrics, used for datatable suggestions + ...(inMetricDimension && { + inMetricDimension, + }), }, }; }) ?? [], @@ -113,11 +121,23 @@ export function getTextBasedDatasource({ if (context && 'dataViewSpec' in context && context.dataViewSpec.title && context.query) { const newLayerId = generateId(); const textBasedQueryColumns = context.textBasedColumns ?? []; + // Number fields are assigned automatically as metrics (!isBucketed). There are cases where the query + // will not return number fields. In these cases we want to suggest a datatable + // Datatable works differently in this case. On the metrics dimension can be all type of fields + const hasNumberTypeColumns = textBasedQueryColumns?.some((c) => c?.meta?.type === 'number'); const newColumns = textBasedQueryColumns.map((c) => { + const inMetricDimension = canColumnBeUsedBeInMetricDimension( + textBasedQueryColumns, + c?.meta?.type + ); return { columnId: c.id, fieldName: c.name, meta: c.meta, + // makes non-number fields to act as metrics, used for datatable suggestions + ...(inMetricDimension && { + inMetricDimension, + }), }; }); @@ -125,13 +145,25 @@ export function getTextBasedDatasource({ const query = context.query; const updatedState = { ...state, + initialContext: undefined, fieldList: textBasedQueryColumns, + ...(context.dataViewSpec.id + ? { + indexPatternRefs: [ + { + id: context.dataViewSpec.id, + title: context.dataViewSpec.title, + timeField: context.dataViewSpec.timeFieldName, + }, + ], + } + : {}), layers: { ...state.layers, [newLayerId]: { index, query, - columns: newColumns ?? [], + columns: newColumns.slice(0, MAX_NUM_OF_COLUMNS) ?? [], allColumns: newColumns ?? [], timeField: context.dataViewSpec.timeFieldName, }, @@ -146,9 +178,10 @@ export function getTextBasedDatasource({ table: { changeType: 'initial' as TableChangeType, isMultiRow: false, + notAssignedMetrics: !hasNumberTypeColumns, layerId: newLayerId, columns: - newColumns?.map((f) => { + newColumns?.slice(0, MAX_NUM_OF_COLUMNS)?.map((f) => { return { columnId: f.columnId, operation: { @@ -304,6 +337,13 @@ export function getTextBasedDatasource({ getLayers(state: TextBasedPrivateState) { return state && state.layers ? Object.keys(state?.layers) : []; }, + // there are cases where a query can return a big amount of columns + // at this case we don't suggest all columns in a table but the first + // MAX_NUM_OF_COLUMNS + suggestsLimitedColumns(state: TextBasedPrivateState) { + const fieldsList = state?.fieldList ?? []; + return fieldsList.length >= MAX_NUM_OF_COLUMNS; + }, isTimeBased: (state, indexPatterns) => { if (!state) return false; const { layers } = state; @@ -382,20 +422,21 @@ export function getTextBasedDatasource({ DimensionEditorComponent: (props: DatasourceDimensionEditorProps) => { const fields = props.state.fieldList; - const selectedField = props.state.layers[props.layerId]?.allColumns?.find( - (column) => column.columnId === props.columnId - ); + const allColumns = props.state.layers[props.layerId]?.allColumns; + const selectedField = allColumns?.find((column) => column.columnId === props.columnId); + const hasNumberTypeColumns = allColumns?.some((c) => c?.meta?.type === 'number'); const updatedFields = fields?.map((f) => { return { ...f, - compatible: props.isMetricDimension - ? props.filterOperations({ - dataType: f.meta.type as DataType, - isBucketed: Boolean(f?.meta?.type !== 'number'), - scale: 'ordinal', - }) - : true, + compatible: + props.isMetricDimension && hasNumberTypeColumns + ? props.filterOperations({ + dataType: f.meta.type as DataType, + isBucketed: Boolean(f?.meta?.type !== 'number'), + scale: 'ordinal', + }) + : true, }; }); return ( @@ -472,7 +513,7 @@ export function getTextBasedDatasource({ }, LayerPanelComponent: (props: DatasourceLayerPanelProps) => { - return ; + return null; }, uniqueLabels(state: TextBasedPrivateState) { @@ -519,6 +560,7 @@ export function getTextBasedDatasource({ dataType: column?.meta?.type as DataType, label: columnLabelMap[columnId] ?? column?.fieldName, isBucketed: Boolean(column?.meta?.type !== 'number'), + inMetricDimension: column.inMetricDimension, hasTimeShift: false, hasReducedTimeRange: false, }; diff --git a/x-pack/plugins/lens/public/datasources/text_based/types.ts b/x-pack/plugins/lens/public/datasources/text_based/types.ts index 8da183f9b9054..4d1f9dfea510f 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/types.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/types.ts @@ -13,6 +13,7 @@ export interface TextBasedLayerColumn { columnId: string; fieldName: string; meta?: DatatableColumn['meta']; + inMetricDimension?: boolean; } export interface TextBasedField { diff --git a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts index 593d34f450212..3a01a7ba9efea 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts @@ -15,6 +15,7 @@ import { loadIndexPatternRefs, getStateFromAggregateQuery, getAllColumns, + canColumnBeUsedBeInMetricDimension, } from './utils'; import type { TextBasedLayerColumn } from './types'; import { type AggregateQuery } from '@kbn/es-query'; @@ -485,4 +486,111 @@ describe('Text based languages utils', () => { }); }); }); + + describe('canColumnBeUsedBeInMetricDimension', () => { + it('should return true if there are non numeric field', async () => { + const fieldList = [ + { + id: 'a', + name: 'Test 1', + meta: { + type: 'string', + }, + }, + { + id: 'b', + name: 'Test 2', + meta: { + type: 'string', + }, + }, + ] as DatatableColumn[]; + const flag = canColumnBeUsedBeInMetricDimension(fieldList, 'string'); + expect(flag).toBeTruthy(); + }); + + it('should return true if there are numeric field and the selected type is number', async () => { + const fieldList = [ + { + id: 'a', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + { + id: 'b', + name: 'Test 2', + meta: { + type: 'string', + }, + }, + ] as DatatableColumn[]; + const flag = canColumnBeUsedBeInMetricDimension(fieldList, 'number'); + expect(flag).toBeTruthy(); + }); + + it('should return false if there are non numeric fields and the selected type is non numeric', async () => { + const fieldList = [ + { + id: 'a', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + { + id: 'b', + name: 'Test 2', + meta: { + type: 'string', + }, + }, + ] as DatatableColumn[]; + const flag = canColumnBeUsedBeInMetricDimension(fieldList, 'date'); + expect(flag).toBeFalsy(); + }); + + it('should return true if there are many columns regardless the types', async () => { + const fieldList = [ + { + id: 'a', + name: 'Test 1', + meta: { + type: 'number', + }, + }, + { + id: 'b', + name: 'Test 2', + meta: { + type: 'number', + }, + }, + { + id: 'c', + name: 'Test 3', + meta: { + type: 'date', + }, + }, + { + id: 'd', + name: 'Test 4', + meta: { + type: 'string', + }, + }, + { + id: 'e', + name: 'Test 5', + meta: { + type: 'string', + }, + }, + ] as DatatableColumn[]; + const flag = canColumnBeUsedBeInMetricDimension(fieldList, 'date'); + expect(flag).toBeTruthy(); + }); + }); }); diff --git a/x-pack/plugins/lens/public/datasources/text_based/utils.ts b/x-pack/plugins/lens/public/datasources/text_based/utils.ts index aea93add73680..ecf4fbcd12ff2 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/utils.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/utils.ts @@ -20,6 +20,8 @@ import { fetchDataFromAggregateQuery } from './fetch_data_from_aggregate_query'; import type { IndexPatternRef, TextBasedPrivateState, TextBasedLayerColumn } from './types'; import type { DataViewsState } from '../../state_management'; +export const MAX_NUM_OF_COLUMNS = 5; + export async function loadIndexPatternRefs( indexPatternsService: DataViewsPublicPluginStart ): Promise { @@ -146,3 +148,25 @@ export function getIndexPatternFromTextBasedQuery(query: AggregateQuery): string return indexPattern; } + +export function canColumnBeDroppedInMetricDimension( + columns: TextBasedLayerColumn[] | DatatableColumn[], + selectedColumnType?: string +): boolean { + // check if at least one numeric field exists + const hasNumberTypeColumns = columns?.some((c) => c?.meta?.type === 'number'); + return !hasNumberTypeColumns || (hasNumberTypeColumns && selectedColumnType === 'number'); +} + +export function canColumnBeUsedBeInMetricDimension( + columns: TextBasedLayerColumn[] | DatatableColumn[], + selectedColumnType?: string +): boolean { + // check if at least one numeric field exists + const hasNumberTypeColumns = columns?.some((c) => c?.meta?.type === 'number'); + return ( + !hasNumberTypeColumns || + columns.length >= MAX_NUM_OF_COLUMNS || + (hasNumberTypeColumns && selectedColumnType === 'number') + ); +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 41184d2212c45..8c31fb2f9d800 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -370,7 +370,7 @@ export function LayerPanels( } }, registerLibraryAnnotationGroup: registerLibraryAnnotationGroupFunction, - isInlineEditing: Boolean(props?.setIsInlineFlyoutFooterVisible), + isInlineEditing: Boolean(props?.setIsInlineFlyoutVisible), })} ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 78a06408902b5..024fb04998d37 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -95,7 +95,7 @@ export function LayerPanel( indexPatternService?: IndexPatternServiceAPI; getUserMessages?: UserMessagesGetter; displayLayerSettings: boolean; - setIsInlineFlyoutFooterVisible?: (status: boolean) => void; + setIsInlineFlyoutVisible?: (status: boolean) => void; } ) { const [activeDimension, setActiveDimension] = useState( @@ -139,7 +139,7 @@ export function LayerPanel( useEffect(() => { // is undefined when the dimension panel is closed const activeDimensionId = activeDimension.activeId; - props?.setIsInlineFlyoutFooterVisible?.(!Boolean(activeDimensionId)); + props?.setIsInlineFlyoutVisible?.(!Boolean(activeDimensionId)); }, [activeDimension.activeId, activeVisualization.id, props]); const panelRef = useRef(null); @@ -394,6 +394,7 @@ export function LayerPanel( )}
    {props.indexPatternService && + !isTextBasedLanguage && (layerDatasource || activeVisualization.LayerPanelComponent) && ( )} @@ -680,7 +681,7 @@ export function LayerPanel( setPanelSettingsOpen(false); return true; }} - isInlineEditing={Boolean(props?.setIsInlineFlyoutFooterVisible)} + isInlineEditing={Boolean(props?.setIsInlineFlyoutVisible)} >
    @@ -749,7 +750,7 @@ export function LayerPanel( isOpen={isDimensionPanelOpen} isFullscreen={isFullscreen} groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')} - isInlineEditing={Boolean(props?.setIsInlineFlyoutFooterVisible)} + isInlineEditing={Boolean(props?.setIsInlineFlyoutVisible)} handleClose={() => { if (layerDatasource) { if (layerDatasource.updateStateOnCloseDimension) { @@ -828,7 +829,7 @@ export function LayerPanel( addLayer: props.addLayer, removeLayer: props.onRemoveLayer, panelRef, - isInlineEditing: Boolean(props?.setIsInlineFlyoutFooterVisible), + isInlineEditing: Boolean(props?.setIsInlineFlyoutVisible), }} />
    diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index 6d06dfb7e6aac..2ef775b51f54b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -29,7 +29,7 @@ export interface ConfigPanelWrapperProps { uiActions: UiActionsStart; getUserMessages?: UserMessagesGetter; hideLayerHeader?: boolean; - setIsInlineFlyoutFooterVisible?: (status: boolean) => void; + setIsInlineFlyoutVisible?: (status: boolean) => void; } export interface LayerPanelProps { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index c1032d144ac33..fd2868b8a4063 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -150,6 +150,7 @@ export function getSuggestions({ return filteredCount || filteredCount === datasourceSuggestion.keptLayerIds.length; }) .flatMap((datasourceSuggestion) => { + const datasourceId = datasourceSuggestion.datasourceId; const table = datasourceSuggestion.table; const currentVisualizationState = visualizationId === activeVisualization?.id ? visualizationState : undefined; @@ -170,7 +171,8 @@ export function getSuggestions({ palette, visualizeTriggerFieldContext && 'isVisualizeAction' in visualizeTriggerFieldContext, activeData, - allowMixed + allowMixed, + datasourceId ); }); }) @@ -240,7 +242,8 @@ function getVisualizationSuggestions( mainPalette?: SuggestionRequest['mainPalette'], isFromContext?: boolean, activeData?: Record, - allowMixed?: boolean + allowMixed?: boolean, + datasourceId?: string ) { try { return visualization @@ -253,6 +256,7 @@ function getVisualizationSuggestions( isFromContext, activeData, allowMixed, + datasourceId, }) .map(({ state, ...visualizationSuggestion }) => ({ ...visualizationSuggestion, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss index f139bbe3ca122..cd2ee706c1e18 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss @@ -1,6 +1,10 @@ @import '../../mixins'; @import '../../variables'; +.lnsSuggestionPanel .euiAccordion__buttonContent { + width: 100%; +} + .lnsSuggestionPanel__suggestions { @include euiScrollBar; @include lnsOverflowShadowHorizontal; @@ -16,14 +20,9 @@ margin-right: -$euiSizeXS; } -.lnsSuggestionPanel { - padding-bottom: $euiSizeS; -} - .lnsSuggestionPanel__button { position: relative; // Let the expression progress indicator position itself against the button flex: 0 0 auto; - width: $lnsSuggestionWidth !important; // sass-lint:disable-line no-important height: $lnsSuggestionHeight; margin-right: $euiSizeS; margin-left: $euiSizeXS / 2; @@ -58,6 +57,10 @@ } } +.lnsSuggestionPanel__button-fixedWidth { + width: $lnsSuggestionWidth !important; // sass-lint:disable-line no-important +} + .lnsSuggestionPanel__suggestionIcon { color: $euiColorDarkShade; width: 100%; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index a4e65b280d203..c487f31fd82ff 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -10,6 +10,7 @@ import './suggestion_panel.scss'; import { camelCase, pick } from 'lodash'; import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { EuiIcon, @@ -20,7 +21,9 @@ import { EuiButtonEmpty, EuiAccordion, EuiText, + EuiNotificationBadge, } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { Ast, fromExpression, toExpression } from '@kbn/interpreter'; import { i18n } from '@kbn/i18n'; @@ -65,6 +68,7 @@ import { selectFramePublicAPI, } from '../../state_management'; import { filterAndSortUserMessages } from '../../app_plugin/get_application_user_messages'; + const MAX_SUGGESTIONS_DISPLAYED = 5; const LOCAL_STORAGE_SUGGESTIONS_PANEL = 'LENS_SUGGESTIONS_PANEL_HIDDEN'; @@ -99,10 +103,13 @@ export interface SuggestionPanelProps { visualizationMap: VisualizationMap; ExpressionRenderer: ReactExpressionRendererType; frame: FramePublicAPI; - getUserMessages: UserMessagesGetter; + getUserMessages?: UserMessagesGetter; nowProvider: DataPublicPluginStart['nowProvider']; core: CoreStart; showOnlyIcons?: boolean; + wrapSuggestions?: boolean; + isAccordionOpen?: boolean; + toggleAccordionCb?: (flag: boolean) => void; } const PreviewRenderer = ({ @@ -165,6 +172,7 @@ const SuggestionPreview = ({ onSelect, showTitleAsLabel, onRender, + wrapSuggestions, }: { onSelect: () => void; preview: { @@ -177,15 +185,30 @@ const SuggestionPreview = ({ selected: boolean; showTitleAsLabel?: boolean; onRender: () => void; + wrapSuggestions?: boolean; }) => { return ( - +
    selectFramePublicAPI(state, datasourceMap)); const changesApplied = useLensSelector(selectChangesApplied); // get user's selection from localStorage, this key defines if the suggestions panel will be hidden or not + const initialAccordionStatusValue = + typeof isAccordionOpen !== 'undefined' ? !Boolean(isAccordionOpen) : false; const [hideSuggestions, setHideSuggestions] = useLocalStorage( LOCAL_STORAGE_SUGGESTIONS_PANEL, - false + initialAccordionStatusValue ); + useEffect(() => { + if (typeof isAccordionOpen !== 'undefined') { + setHideSuggestions(!Boolean(isAccordionOpen)); + } + }, [isAccordionOpen, setHideSuggestions]); const toggleSuggestions = useCallback(() => { setHideSuggestions(!hideSuggestions); - }, [setHideSuggestions, hideSuggestions]); + toggleAccordionCb?.(!hideSuggestions); + }, [setHideSuggestions, hideSuggestions, toggleAccordionCb]); const missingIndexPatterns = getMissingIndexPattern( activeDatasourceId ? datasourceMap[activeDatasourceId] : null, @@ -304,8 +338,10 @@ export function SuggestionPanel({ ), })); - const hasErrors = - getUserMessages(['visualization', 'visualizationInEditor'], { severity: 'error' }).length > 0; + const hasErrors = getUserMessages + ? getUserMessages(['visualization', 'visualizationInEditor'], { severity: 'error' }).length > + 0 + : false; const newStateExpression = currentVisualization.state && currentVisualization.activeId && !hasErrors @@ -450,6 +486,7 @@ export function SuggestionPanel({ selected={lastSelectedSuggestion === -1} showTitleAsLabel onRender={() => onSuggestionRender(0)} + wrapSuggestions={wrapSuggestions} /> )} {!hideSuggestions && @@ -474,64 +511,88 @@ export function SuggestionPanel({ selected={index === lastSelectedSuggestion} onRender={() => onSuggestionRender(index + 1)} showTitleAsLabel={showOnlyIcons} + wrapSuggestions={wrapSuggestions} /> ); })} ); }; - + const title = ( + +

    + +

    +
    + ); return ( -
    - -

    - -

    - - } - forceState={hideSuggestions ? 'closed' : 'open'} - onToggle={toggleSuggestions} - extraAction={ - existsStagedPreview && - !hideSuggestions && ( - - { - dispatchLens(submitSuggestion()); - }} - > - {i18n.translate('xpack.lens.sugegstion.refreshSuggestionLabel', { - defaultMessage: 'Refresh', + + {existsStagedPreview && ( + - - ) - } + > + { + dispatchLens(submitSuggestion()); + }} + > + {i18n.translate('xpack.lens.sugegstion.refreshSuggestionLabel', { + defaultMessage: 'Refresh', + })} + + + )} + {wrapSuggestions && ( + + {suggestions.length + 1} + + )} + + ) + } + > +
    -
    - {changesApplied ? renderSuggestionsUI() : renderApplyChangesPrompt()} -
    - -
    + {changesApplied ? renderSuggestionsUI() : renderApplyChangesPrompt()} +
    + ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss index 391361570d04b..2c0d374a7d07f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss @@ -149,3 +149,10 @@ 75% { transform: translateY(15%); } 100% { transform: translateY(10%); } } + +.lnsVisualizationToolbar--fixed { + position: fixed; + width: 100%; + z-index: 1; + background-color: $euiColorLightestShade; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index ea2c8d27086fb..f5ee47cbcd294 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -53,10 +53,11 @@ export interface WorkspacePanelWrapperProps { export function VisualizationToolbar(props: { activeVisualization: Visualization | null; framePublicAPI: FramePublicAPI; + isFixedPosition?: boolean; }) { const dispatchLens = useLensDispatch(); const visualization = useLensSelector(selectVisualizationState); - const { activeVisualization } = props; + const { activeVisualization, isFixedPosition } = props; const setVisualizationState = useCallback( (newState: unknown) => { if (!activeVisualization) { @@ -77,7 +78,12 @@ export function VisualizationToolbar(props: { return ( <> {ToolbarComponent && ( - + {ToolbarComponent({ frame: props.framePublicAPI, state: visualization.state, diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index bb99e061893f2..16d730139c14a 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -747,7 +747,11 @@ export class Embeddable * Gets the Lens embeddable's datasource and visualization states * updates the embeddable input */ - async updateVisualization(datasourceState: unknown, visualizationState: unknown) { + async updateVisualization( + datasourceState: unknown, + visualizationState: unknown, + visualizationType?: string + ) { const viz = this.savedVis; const activeDatasourceId = (this.activeDatasourceId ?? 'formBased') as EditLensConfigurationProps['datasourceId']; @@ -769,7 +773,7 @@ export class Embeddable ), visualizationState, activeVisualization: this.activeVisualizationId - ? this.deps.visualizationMap[this.activeVisualizationId] + ? this.deps.visualizationMap[visualizationType ?? this.activeVisualizationId] : undefined, }); const attrs = { @@ -780,6 +784,7 @@ export class Embeddable datasourceStates, }, references, + visualizationType: visualizationType ?? viz.visualizationType, }; /** @@ -795,6 +800,15 @@ export class Embeddable } } + async updateSuggestion(attrs: LensSavedObjectAttributes) { + const viz = this.savedVis; + const newViz = { + ...viz, + ...attrs, + }; + this.updateInput({ attributes: newViz }); + } + /** * Callback which allows the navigation to the editor. * Used for the Edit in Lens link inside the inline editing flyout. @@ -848,6 +862,7 @@ export class Embeddable ); } diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index d93e3729a6541..986f2f65c693e 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -9,6 +9,7 @@ import React, { FC, useEffect } from 'react'; import type { CoreStart } from '@kbn/core/public'; import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; +import { PanelLoader } from '@kbn/panel-loader'; import { EuiLoadingChart } from '@elastic/eui'; import { EmbeddableFactory, @@ -160,7 +161,7 @@ const EmbeddablePanelWrapper: FC = ({ }, [embeddable, input]); if (loading || !embeddable) { - return ; + return ; } return ( diff --git a/x-pack/plugins/lens/public/mocks/dataview_mock.ts b/x-pack/plugins/lens/public/mocks/dataview_mock.ts new file mode 100644 index 0000000000000..880b9a3f287db --- /dev/null +++ b/x-pack/plugins/lens/public/mocks/dataview_mock.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataView } from '@kbn/data-views-plugin/public'; +import { buildDataViewMock } from '@kbn/discover-utils/src/__mocks__'; + +const fields = [ + { + name: '_index', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: '@timestamp', + displayName: 'timestamp', + type: 'date', + scripted: false, + filterable: true, + aggregatable: true, + sortable: true, + }, + { + name: 'message', + displayName: 'message', + type: 'string', + scripted: false, + filterable: false, + }, + { + name: 'extension', + displayName: 'extension', + type: 'string', + scripted: false, + filterable: true, + aggregatable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + scripted: false, + filterable: true, + aggregatable: true, + }, +] as DataView['fields']; + +export const mockDataViewWithTimefield = buildDataViewMock({ + name: 'index-pattern-with-timefield', + fields, + timeFieldName: '%timestamp', +}); diff --git a/x-pack/plugins/lens/public/mocks/index.ts b/x-pack/plugins/lens/public/mocks/index.ts index 6cc3ff02ff92e..f90ecc9b99fe9 100644 --- a/x-pack/plugins/lens/public/mocks/index.ts +++ b/x-pack/plugins/lens/public/mocks/index.ts @@ -29,6 +29,8 @@ export { renderWithReduxStore, } from './store_mocks'; export { lensPluginMock } from './lens_plugin_mock'; +export { mockDataViewWithTimefield } from './dataview_mock'; +export { mockAllSuggestions } from './suggestions_mock'; export type FrameMock = jest.Mocked; diff --git a/x-pack/plugins/lens/public/mocks/suggestions_mock.ts b/x-pack/plugins/lens/public/mocks/suggestions_mock.ts new file mode 100644 index 0000000000000..0ed32fbfd84da --- /dev/null +++ b/x-pack/plugins/lens/public/mocks/suggestions_mock.ts @@ -0,0 +1,291 @@ +/* + * Copyright 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 { Suggestion } from '../types'; + +export const currentSuggestionMock = { + title: 'Heat map', + hide: false, + score: 0.6, + previewIcon: 'heatmap', + visualizationId: 'lnsHeatmap', + visualizationState: { + shape: 'heatmap', + layerId: '46aa21fa-b747-4543-bf90-0b40007c546d', + layerType: 'data', + legend: { + isVisible: true, + position: 'right', + type: 'heatmap_legend', + }, + gridConfig: { + type: 'heatmap_grid', + isCellLabelVisible: false, + isYAxisLabelVisible: true, + isXAxisLabelVisible: true, + isYAxisTitleVisible: false, + isXAxisTitleVisible: false, + }, + valueAccessor: '5b9b8b76-0836-4a12-b9c0-980c9900502f', + xAccessor: '81e332d6-ee37-42a8-a646-cea4fc75d2d3', + }, + keptLayerIds: ['46aa21fa-b747-4543-bf90-0b40007c546d'], + datasourceState: { + layers: { + '46aa21fa-b747-4543-bf90-0b40007c546d': { + index: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + query: { + esql: 'FROM kibana_sample_data_flights | keep Dest, AvgTicketPrice', + }, + columns: [ + { + columnId: '81e332d6-ee37-42a8-a646-cea4fc75d2d3', + fieldName: 'Dest', + meta: { + type: 'string', + }, + }, + { + columnId: '5b9b8b76-0836-4a12-b9c0-980c9900502f', + fieldName: 'AvgTicketPrice', + meta: { + type: 'number', + }, + }, + ], + allColumns: [ + { + columnId: '81e332d6-ee37-42a8-a646-cea4fc75d2d3', + fieldName: 'Dest', + meta: { + type: 'string', + }, + }, + { + columnId: '5b9b8b76-0836-4a12-b9c0-980c9900502f', + fieldName: 'AvgTicketPrice', + meta: { + type: 'number', + }, + }, + ], + timeField: 'timestamp', + }, + }, + fieldList: [], + indexPatternRefs: [], + initialContext: { + dataViewSpec: { + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + version: 'WzM1ODA3LDFd', + title: 'kibana_sample_data_flights', + timeFieldName: 'timestamp', + sourceFilters: [], + fields: { + AvgTicketPrice: { + count: 0, + name: 'AvgTicketPrice', + type: 'number', + esTypes: ['float'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'number', + params: { + pattern: '$0,0.[00]', + }, + }, + shortDotsEnable: false, + isMapped: true, + }, + Dest: { + count: 0, + name: 'Dest', + type: 'string', + esTypes: ['keyword'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'string', + }, + shortDotsEnable: false, + isMapped: true, + }, + timestamp: { + count: 0, + name: 'timestamp', + type: 'date', + esTypes: ['date'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'date', + }, + shortDotsEnable: false, + isMapped: true, + }, + }, + allowNoIndex: false, + name: 'Kibana Sample Data Flights', + }, + fieldName: '', + contextualFields: ['Dest', 'AvgTicketPrice'], + query: { + esql: 'FROM "kibana_sample_data_flights"', + }, + }, + }, + datasourceId: 'textBased', + columns: 2, + changeType: 'initial', +} as Suggestion; + +export const mockAllSuggestions = [ + currentSuggestionMock, + { + title: 'Donut', + score: 0.46, + visualizationId: 'lnsPie', + previewIcon: 'pie', + visualizationState: { + shape: 'donut', + layers: [ + { + layerId: '2513a3d4-ad9d-48ea-bd58-8b6419ab97e6', + primaryGroups: ['923f0681-3fe1-4987-aa27-d9c91fb95fa6'], + metrics: ['b5f41c04-4bca-4abe-ae5c-b1d4d6fb00e0'], + numberDisplay: 'percent', + categoryDisplay: 'default', + legendDisplay: 'default', + nestedLegend: false, + layerType: 'data', + }, + ], + }, + keptLayerIds: ['2513a3d4-ad9d-48ea-bd58-8b6419ab97e6'], + datasourceState: { + layers: { + '2513a3d4-ad9d-48ea-bd58-8b6419ab97e6': { + index: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + query: { + esql: 'FROM "kibana_sample_data_flights"', + }, + columns: [ + { + columnId: '923f0681-3fe1-4987-aa27-d9c91fb95fa6', + fieldName: 'Dest', + meta: { + type: 'string', + }, + }, + { + columnId: 'b5f41c04-4bca-4abe-ae5c-b1d4d6fb00e0', + fieldName: 'AvgTicketPrice', + meta: { + type: 'number', + }, + }, + ], + allColumns: [ + { + columnId: '923f0681-3fe1-4987-aa27-d9c91fb95fa6', + fieldName: 'Dest', + meta: { + type: 'string', + }, + }, + { + columnId: 'b5f41c04-4bca-4abe-ae5c-b1d4d6fb00e0', + fieldName: 'AvgTicketPrice', + meta: { + type: 'number', + }, + }, + ], + timeField: 'timestamp', + }, + }, + fieldList: [], + indexPatternRefs: [], + initialContext: { + dataViewSpec: { + id: 'd3d7af60-4c81-11e8-b3d7-01146121b73d', + version: 'WzM1ODA3LDFd', + title: 'kibana_sample_data_flights', + timeFieldName: 'timestamp', + sourceFilters: [], + fields: { + AvgTicketPrice: { + count: 0, + name: 'AvgTicketPrice', + type: 'number', + esTypes: ['float'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'number', + params: { + pattern: '$0,0.[00]', + }, + }, + shortDotsEnable: false, + isMapped: true, + }, + Dest: { + count: 0, + name: 'Dest', + type: 'string', + esTypes: ['keyword'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'string', + }, + shortDotsEnable: false, + isMapped: true, + }, + timestamp: { + count: 0, + name: 'timestamp', + type: 'date', + esTypes: ['date'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + format: { + id: 'date', + }, + shortDotsEnable: false, + isMapped: true, + }, + }, + typeMeta: {}, + allowNoIndex: false, + name: 'Kibana Sample Data Flights', + }, + fieldName: '', + contextualFields: ['Dest', 'AvgTicketPrice'], + query: { + esql: 'FROM "kibana_sample_data_flights"', + }, + }, + }, + datasourceId: 'textBased', + columns: 2, + changeType: 'unchanged', + } as Suggestion, +]; diff --git a/x-pack/plugins/lens/public/shared_components/dataview_picker/dataview_picker.tsx b/x-pack/plugins/lens/public/shared_components/dataview_picker/dataview_picker.tsx index 9d55284cc36c8..c4efd626d4772 100644 --- a/x-pack/plugins/lens/public/shared_components/dataview_picker/dataview_picker.tsx +++ b/x-pack/plugins/lens/public/shared_components/dataview_picker/dataview_picker.tsx @@ -6,9 +6,11 @@ */ import { i18n } from '@kbn/i18n'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; import React, { useState } from 'react'; import { EuiPopover, EuiPopoverTitle, EuiSelectableProps } from '@elastic/eui'; import { DataViewsList } from '@kbn/unified-search-plugin/public'; +import { css } from '@emotion/react'; import { type IndexPatternRef } from '../../types'; import { type ChangeIndexPatternTriggerProps, TriggerButton } from './trigger'; @@ -30,43 +32,47 @@ export function ChangeIndexPattern({ const [isPopoverOpen, setPopoverIsOpen] = useState(false); return ( - <> - setPopoverIsOpen(!isPopoverOpen)} - /> - } - panelProps={{ - ['data-test-subj']: 'lnsChangeIndexPatternPopover', - }} - isOpen={isPopoverOpen} - closePopover={() => setPopoverIsOpen(false)} - display="block" - panelPaddingSize="none" - ownFocus + setPopoverIsOpen(!isPopoverOpen)} + /> + } + panelProps={{ + ['data-test-subj']: 'lnsChangeIndexPatternPopover', + }} + isOpen={isPopoverOpen} + closePopover={() => setPopoverIsOpen(false)} + display="block" + panelPaddingSize="none" + ownFocus + > +
    -
    - - {i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', { - defaultMessage: 'Data view', - })} - + + {i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', { + defaultMessage: 'Data view', + })} + - { - onChangeIndexPattern(newId); - setPopoverIsOpen(false); - }} - currentDataViewId={indexPatternId} - selectableProps={selectableProps} - /> -
    - - + { + onChangeIndexPattern(newId); + setPopoverIsOpen(false); + }} + currentDataViewId={indexPatternId} + selectableProps={selectableProps} + /> +
    +
    ); } diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.scss b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.scss index 56cfe41c4b889..6572fd969a5be 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.scss +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.scss @@ -1,6 +1,7 @@ // styles needed to display extra drop targets that are outside of the config panel main area while also allowing to scroll vertically .lnsConfigPanel__overlay { clip-path: polygon(-100% 0, 100% 0, 100% 100%, -100% 100%); + background: $euiColorLightestShade; .kbnOverlayMountWrapper { padding-left: $euiFormMaxWidth; margin-left: -$euiFormMaxWidth; diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts index b1647876581fd..8fd011fddfb2e 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts @@ -52,6 +52,7 @@ export async function executeAction({ embeddable, startDependencies, overlays, t size: 's', 'data-test-subj': 'customizeLens', type: 'push', + paddingSize: 'm', hideCloseButton: true, onClose: (overlayRef) => { if (overlayTracker) overlayTracker.clearOverlays(); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 25b637aebf071..e5c9fad96d6ca 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -198,6 +198,8 @@ export interface TableSuggestion { * The change type indicates what was changed in this table compared to the currently active table of this layer. */ changeType: TableChangeType; + + notAssignedMetrics?: boolean; } /** @@ -509,6 +511,8 @@ export interface Datasource { ) => Promise; injectReferencesToLayers?: (state: T, references?: SavedObjectReference[]) => T; + + suggestsLimitedColumns?: (state: T) => boolean; } export interface DatasourceFixAction { @@ -746,6 +750,7 @@ export interface OperationMetadata { export interface OperationDescriptor extends Operation { hasTimeShift: boolean; hasReducedTimeRange: boolean; + inMetricDimension?: boolean; } export interface VisualizationConfigProps { @@ -882,6 +887,7 @@ export interface SuggestionRequest { subVisualizationId?: string; activeData?: Record; allowMixed?: boolean; + datasourceId?: string; } /** diff --git a/x-pack/plugins/lens/public/vis_type_alias.ts b/x-pack/plugins/lens/public/vis_type_alias.ts index 2fc493df38edc..20822ee76a094 100644 --- a/x-pack/plugins/lens/public/vis_type_alias.ts +++ b/x-pack/plugins/lens/public/vis_type_alias.ts @@ -11,8 +11,10 @@ import { getBasePath, getEditPath } from '../common/constants'; import { getLensClient } from './persistence/lens_client'; export const getLensAliasConfig = (): VisTypeAlias => ({ - aliasPath: getBasePath(), - aliasApp: 'lens', + alias: { + path: getBasePath(), + app: 'lens', + }, name: 'lens', promotion: true, title: i18n.translate('xpack.lens.visTypeAlias.title', { @@ -41,8 +43,7 @@ export const getLensAliasConfig = (): VisTypeAlias => ({ title, description, updatedAt, - editUrl: getEditPath(id), - editApp: 'lens', + editor: { editUrl: getEditPath(id), editApp: 'lens' }, icon: 'lensApp', stage: 'production', savedObjectType: type, diff --git a/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx index a6810e77d4388..a3f4f6f797ee1 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx @@ -138,6 +138,26 @@ describe('Datatable Visualization', () => { expect(suggestions.length).toBeGreaterThan(0); }); + it('should force table as suggestion when there are no number fields', () => { + const suggestions = datatableVisualization.getSuggestions({ + state: { + layerId: 'first', + layerType: LayerTypes.DATA, + columns: [{ columnId: 'col1' }], + }, + table: { + isMultiRow: true, + layerId: 'first', + changeType: 'initial', + columns: [strCol('col1'), strCol('col2')], + notAssignedMetrics: true, + }, + keptLayerIds: [], + }); + + expect(suggestions.length).toBeGreaterThan(0); + }); + it('should reject suggestion with static value', () => { function staticValueCol(columnId: string): TableSuggestionColumn { return { @@ -387,6 +407,48 @@ describe('Datatable Visualization', () => { }).groups[2].accessors ).toEqual([{ columnId: 'c' }, { columnId: 'b' }]); }); + + it('should compute the groups correctly for text based languages', () => { + const datasource = createMockDatasource('textBased', { + isTextBasedLanguage: jest.fn(() => true), + }); + datasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, + ]); + const frame = mockFrame(); + frame.datasourceLayers = { first: datasource.publicAPIMock }; + + const groups = datatableVisualization.getConfiguration({ + layerId: 'first', + state: { + layerId: 'first', + layerType: LayerTypes.DATA, + columns: [{ columnId: 'b', isMetric: true }, { columnId: 'c' }], + }, + frame, + }).groups; + + // rows + expect(groups[0].accessors).toEqual([ + { + columnId: 'c', + triggerIconType: undefined, + }, + ]); + + // columns + expect(groups[1].accessors).toEqual([]); + + // metrics + expect(groups[2].accessors).toEqual([ + { + columnId: 'b', + triggerIconType: undefined, + palette: undefined, + }, + ]); + }); }); describe('#removeDimension', () => { @@ -462,7 +524,11 @@ describe('Datatable Visualization', () => { ).toEqual({ layerId: 'layer1', layerType: LayerTypes.DATA, - columns: [{ columnId: 'b' }, { columnId: 'c' }, { columnId: 'd', isTransposed: false }], + columns: [ + { columnId: 'b' }, + { columnId: 'c' }, + { columnId: 'd', isTransposed: false, isMetric: false }, + ], }); }); @@ -482,7 +548,7 @@ describe('Datatable Visualization', () => { ).toEqual({ layerId: 'layer1', layerType: LayerTypes.DATA, - columns: [{ columnId: 'b', isTransposed: false }, { columnId: 'c' }], + columns: [{ columnId: 'b', isTransposed: false, isMetric: false }, { columnId: 'c' }], }); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx index 35757771af754..505b20bdc3e58 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/visualization.tsx @@ -165,11 +165,14 @@ export const getDatatableVisualization = ({ ? 0.5 : 1; + // forcing datatable as a suggestion when there are no metrics (number fields) + const forceSuggestion = Boolean(table?.notAssignedMetrics); + return [ { title, // table with >= 10 columns will have a score of 0.4, fewer columns reduce score - score: (Math.min(table.columns.length, 10) / 10) * 0.4 * changeFactor, + score: forceSuggestion ? 1 : (Math.min(table.columns.length, 10) / 10) * 0.4 * changeFactor, state: { ...(state || {}), layerId: table.layerId, @@ -187,6 +190,13 @@ export const getDatatableVisualization = ({ ]; }, + /* + Datatable works differently on text based datasource and form based + - Form based: It relies on the isBucketed flag to identify groups. It allows only numeric fields + on the Metrics dimension + - Text based: It relies on the isMetric flag to identify groups. It allows all type of fields + on the Metric dimension in cases where there are no numeric columns + **/ getConfiguration({ state, frame, layerId }) { const { sortedColumns, datasource } = getDataSourceAndSortedColumns(state, frame.datasourceLayers, layerId) || {}; @@ -199,9 +209,11 @@ export const getDatatableVisualization = ({ if (!sortedColumns) { return { groups: [] }; } + const isTextBasedLanguage = datasource?.isTextBasedLanguage(); return { groups: [ + // In this group we get columns that are not transposed and are not on the metric dimension { groupId: 'rows', groupLabel: i18n.translate('xpack.lens.datatable.breakdownRows', { @@ -216,11 +228,17 @@ export const getDatatableVisualization = ({ }), layerId: state.layerId, accessors: sortedColumns - .filter( - (c) => - datasource!.getOperationForColumnId(c)?.isBucketed && - !state.columns.find((col) => col.columnId === c)?.isTransposed - ) + .filter((c) => { + const column = state.columns.find((col) => col.columnId === c); + if (isTextBasedLanguage) { + return ( + !datasource!.getOperationForColumnId(c)?.inMetricDimension && + !column?.isMetric && + !column?.isTransposed + ); + } + return datasource!.getOperationForColumnId(c)?.isBucketed && !column?.isTransposed; + }) .map((accessor) => ({ columnId: accessor, triggerIconType: columnMap[accessor].hidden @@ -236,6 +254,7 @@ export const getDatatableVisualization = ({ hideGrouping: true, nestingOrder: 1, }, + // In this group we get columns that are transposed and are not on the metric dimension { groupId: 'columns', groupLabel: i18n.translate('xpack.lens.datatable.breakdownColumns', { @@ -250,11 +269,15 @@ export const getDatatableVisualization = ({ }), layerId: state.layerId, accessors: sortedColumns - .filter( - (c) => + .filter((c) => { + if (isTextBasedLanguage) { + return state.columns.find((col) => col.columnId === c)?.isTransposed; + } + return ( datasource!.getOperationForColumnId(c)?.isBucketed && state.columns.find((col) => col.columnId === c)?.isTransposed - ) + ); + }) .map((accessor) => ({ columnId: accessor })), supportsMoreColumns: true, filterOperations: (op) => op.isBucketed, @@ -263,6 +286,7 @@ export const getDatatableVisualization = ({ hideGrouping: true, nestingOrder: 0, }, + // In this group we get columns are on the metric dimension { groupId: 'metrics', groupLabel: i18n.translate('xpack.lens.datatable.metrics', { @@ -278,7 +302,16 @@ export const getDatatableVisualization = ({ }, layerId: state.layerId, accessors: sortedColumns - .filter((c) => !datasource!.getOperationForColumnId(c)?.isBucketed) + .filter((c) => { + const operation = datasource!.getOperationForColumnId(c); + if (isTextBasedLanguage) { + return ( + operation?.inMetricDimension || + state.columns.find((col) => col.columnId === c)?.isMetric + ); + } + return !operation?.isBucketed; + }) .map((accessor) => { const columnConfig = columnMap[accessor]; const stops = columnConfig?.palette?.params?.stops; @@ -316,7 +349,12 @@ export const getDatatableVisualization = ({ ...prevState, columns: prevState.columns.map((column) => { if (column.columnId === columnId || column.columnId === previousColumn) { - return { ...column, columnId, isTransposed: groupId === 'columns' }; + return { + ...column, + columnId, + isTransposed: groupId === 'columns', + isMetric: groupId === 'metrics', + }; } return column; }), @@ -324,7 +362,10 @@ export const getDatatableVisualization = ({ } return { ...prevState, - columns: [...prevState.columns, { columnId, isTransposed: groupId === 'columns' }], + columns: [ + ...prevState.columns, + { columnId, isTransposed: groupId === 'columns', isMetric: groupId === 'metrics' }, + ], }; }, removeDimension({ prevState, columnId }) { @@ -371,9 +412,11 @@ export const getDatatableVisualization = ({ ): Ast | null { const { sortedColumns, datasource } = getDataSourceAndSortedColumns(state, datasourceLayers, state.layerId) || {}; + const isTextBasedLanguage = datasource?.isTextBasedLanguage(); if ( sortedColumns?.length && + !isTextBasedLanguage && sortedColumns.filter((c) => !datasource!.getOperationForColumnId(c)?.isBucketed).length === 0 ) { return null; @@ -435,6 +478,15 @@ export const getDatatableVisualization = ({ const canColor = datasource!.getOperationForColumnId(column.columnId)?.dataType === 'number'; + let isTransposable = + !isTextBasedLanguage && + !datasource!.getOperationForColumnId(column.columnId)?.isBucketed; + + if (isTextBasedLanguage) { + const operation = datasource!.getOperationForColumnId(column.columnId); + isTransposable = Boolean(column?.isMetric || operation?.inMetricDimension); + } + const datatableColumnFn = buildExpressionFunction( 'lens_datatable_column', { @@ -443,8 +495,7 @@ export const getDatatableVisualization = ({ oneClickFilter: column.oneClickFilter, width: column.width, isTransposed: column.isTransposed, - transposable: !datasource!.getOperationForColumnId(column.columnId)?.isBucketed, - alignment: column.alignment, + transposable: isTransposable, colorMode: canColor && column.colorMode ? column.colorMode : 'none', palette: paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams), summaryRow: hasNoSummaryRow ? undefined : column.summaryRow!, diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts index ff631ee605a59..8a89bca65a2d9 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.test.ts @@ -305,6 +305,70 @@ describe('heatmap suggestions', () => { }, ]); }); + + test('when no metric dimension but groups', () => { + expect( + getSuggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'date-column', + operation: { + isBucketed: true, + dataType: 'date', + scale: 'interval', + label: 'Date', + }, + }, + { + columnId: 'string-column-01', + operation: { + isBucketed: true, + dataType: 'string', + label: 'Bucket 1', + }, + }, + ], + changeType: 'initial', + }, + state: { + layerId: 'first', + layerType: LayerTypes.DATA, + } as HeatmapVisualizationState, + keptLayerIds: ['first'], + }) + ).toEqual([ + { + state: { + layerId: 'first', + layerType: LayerTypes.DATA, + shape: 'heatmap', + xAccessor: 'date-column', + yAccessor: 'string-column-01', + gridConfig: { + type: HEATMAP_GRID_FUNCTION, + isCellLabelVisible: false, + isYAxisLabelVisible: true, + isXAxisLabelVisible: true, + isYAxisTitleVisible: false, + isXAxisTitleVisible: false, + }, + legend: { + isVisible: true, + position: Position.Right, + type: LEGEND_FUNCTION, + }, + }, + title: 'Heat map', + hide: true, + incomplete: true, + previewIcon: IconChartHeatmap, + score: 0, + }, + ]); + }); test('for tables with a single bucket dimension', () => { expect( getSuggestions({ diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts index ccb9c1014c25b..3f748485bdb03 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/heatmap/suggestions.ts @@ -61,6 +61,7 @@ export const getSuggestions: Visualization['getSugges const isSingleBucketDimension = groups.length === 1 && metrics.length === 0; const isOnlyMetricDimension = groups.length === 0 && metrics.length === 1; + const isOnlyBucketDimension = groups.length > 0 && metrics.length === 0; /** * Hide for: @@ -77,6 +78,7 @@ export const getSuggestions: Visualization['getSugges table.changeType === 'reorder' || isSingleBucketDimension || hasOnlyDatehistogramBuckets || + isOnlyBucketDimension || isOnlyMetricDimension; const newState: HeatmapVisualizationState = { @@ -130,7 +132,7 @@ export const getSuggestions: Visualization['getSugges hide, previewIcon: IconChartHeatmap, score: Number(score.toFixed(1)), - incomplete: isSingleBucketDimension || isOnlyMetricDimension, + incomplete: isSingleBucketDimension || isOnlyMetricDimension || isOnlyBucketDimension, }, ]; }; diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts index e1803e106f2eb..e4b4dc1879456 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.test.ts @@ -98,6 +98,7 @@ describe('metric_suggestions', () => { ).map((table) => expect(getSuggestions({ table, keptLayerIds: ['l1'] })).toEqual([])) ); }); + test('does not suggest for a static value', () => { const suggestion = getSuggestions({ table: { @@ -133,6 +134,29 @@ describe('metric_suggestions', () => { expect(suggestion).toHaveLength(0); }); + + test('does not suggest for text based languages', () => { + const col = { + columnId: 'id', + operation: { + dataType: 'number', + label: `Top values`, + isBucketed: false, + }, + } as const; + const suggestion = getSuggestions({ + table: { + columns: [col], + isMultiRow: false, + layerId: 'l1', + changeType: 'unchanged', + }, + keptLayerIds: [], + datasourceId: 'textBased', + }); + + expect(suggestion).toHaveLength(0); + }); test('suggests a basic metric chart', () => { const [suggestion, ...rest] = getSuggestions({ table: { diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts index c48e463ec83df..7e00322fedcd6 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/metric_suggestions.ts @@ -20,6 +20,7 @@ export function getSuggestions({ table, state, keptLayerIds, + datasourceId, }: SuggestionRequest): Array> { // We only render metric charts for single-row queries. We require a single, numeric column. if ( @@ -39,6 +40,11 @@ export function getSuggestions({ return []; } + // do not return the legacy metric vis for the textbased mode (i.e. ES|QL) + if (datasourceId === 'textBased') { + return []; + } + return [getSuggestion(table)]; } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx index 99c7b2bec30d4..30ffc3a32b1f7 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/layer_header.tsx @@ -170,7 +170,6 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps) { return ( setPopoverIsOpen(!isPopoverOpen)} @@ -188,7 +187,11 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps) { defaultMessage: 'Layer visualization type', })} -
    +
    { @@ -443,69 +442,5 @@ describe('licensing plugin', () => { expect(removeInterceptorMock).toHaveBeenCalledTimes(1); }); - - it('registers the subscription upsell events', async () => { - const sessionStorage = coreMock.createStorage(); - plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage); - - const coreSetup = coreMock.createSetup(); - - await plugin.setup(coreSetup); - await plugin.stop(); - - expect(findRegisteredEventTypeByName('subscription__upsell__click', coreSetup.analytics)) - .toMatchInlineSnapshot(` - Array [ - Object { - "eventType": "subscription__upsell__click", - "schema": Object { - "feature": Object { - "_meta": Object { - "description": "A human-readable identifier describing the feature that is being promoted", - }, - "type": "keyword", - }, - "source": Object { - "_meta": Object { - "description": "A human-readable identifier describing the location of the beginning of the subscription flow", - }, - "type": "keyword", - }, - }, - }, - ] - `); - expect(findRegisteredEventTypeByName('subscription__upsell__impression', coreSetup.analytics)) - .toMatchInlineSnapshot(` - Array [ - Object { - "eventType": "subscription__upsell__impression", - "schema": Object { - "feature": Object { - "_meta": Object { - "description": "A human-readable identifier describing the feature that is being promoted", - }, - "type": "keyword", - }, - "source": Object { - "_meta": Object { - "description": "A human-readable identifier describing the location of the beginning of the subscription flow", - }, - "type": "keyword", - }, - }, - }, - ] - `); - }); }); }); - -function findRegisteredEventTypeByName( - eventTypeName: string, - analyticsClientMock: jest.Mocked -) { - return analyticsClientMock.registerEventType.mock.calls.find( - ([{ eventType }]) => eventType === eventTypeName - )!; -} diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index bc350675d7dd2..3953a29a08214 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -8,7 +8,6 @@ import { Observable, Subject, Subscription } from 'rxjs'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import { registerEvents as registerSubscriptionTrackingEvents } from '@kbn/subscription-tracking'; import { ILicense } from '../common/types'; import { LicensingPluginSetup, LicensingPluginStart } from './types'; import { createLicenseUpdate } from '../common/license_update'; @@ -85,7 +84,6 @@ export class LicensingPlugin implements Plugin { if (license.isAvailable) { diff --git a/x-pack/plugins/licensing/tsconfig.json b/x-pack/plugins/licensing/tsconfig.json index 1deb735f99466..804bf057afa6d 100644 --- a/x-pack/plugins/licensing/tsconfig.json +++ b/x-pack/plugins/licensing/tsconfig.json @@ -14,8 +14,6 @@ "@kbn/std", "@kbn/i18n", "@kbn/analytics-client", - "@kbn/subscription-tracking", - "@kbn/core-analytics-browser", "@kbn/logging-mocks" ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts index 4ebfcd0fec910..529e1bac24aff 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts @@ -53,18 +53,19 @@ export const getExceptionListItemSchemaMock = ( * This is useful for end to end tests where we remove the auto generated parts for comparisons * such as created_at, updated_at, and id. */ -export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = - (): Partial => ({ - comments: [], - created_by: ELASTIC_USER, - description: DESCRIPTION, - entries: ENTRIES, - item_id: ITEM_ID, - list_id: LIST_ID, - name: NAME, - namespace_type: 'single', - os_types: OS_TYPES, - tags: [], - type: ITEM_TYPE, - updated_by: ELASTIC_USER, - }); +export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = ( + elasticUser: string = ELASTIC_USER +): Partial => ({ + comments: [], + created_by: elasticUser, + description: DESCRIPTION, + entries: ENTRIES, + item_id: ITEM_ID, + list_id: LIST_ID, + name: NAME, + namespace_type: 'single', + os_types: OS_TYPES, + tags: [], + type: ITEM_TYPE, + updated_by: elasticUser, +}); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts index eca17b4c835d6..cdf86c2cc720d 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts @@ -82,17 +82,18 @@ export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => { * This is useful for end to end tests where we remove the auto generated parts for comparisons * such as created_at, updated_at, and id. */ -export const getExceptionResponseMockWithoutAutoGeneratedValues = - (): Partial => ({ - created_by: ELASTIC_USER, - description: DESCRIPTION, - immutable: IMMUTABLE, - list_id: LIST_ID, - name: NAME, - namespace_type: 'single', - os_types: [], - tags: [], - type: ENDPOINT_TYPE, - updated_by: ELASTIC_USER, - version: VERSION, - }); +export const getExceptionResponseMockWithoutAutoGeneratedValues = ( + elasticUser: string = ELASTIC_USER +): Partial => ({ + created_by: elasticUser, + description: DESCRIPTION, + immutable: IMMUTABLE, + list_id: LIST_ID, + name: NAME, + namespace_type: 'single', + os_types: [], + tags: [], + type: ENDPOINT_TYPE, + updated_by: elasticUser, + version: VERSION, +}); diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts index cce497f87335c..4d773fa63306f 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts @@ -40,10 +40,12 @@ export const getListItemResponseMock = (): ListItemSchema => ({ * This is useful for end to end tests where we remove the auto generated parts for comparisons * such as created_at, updated_at, and id. */ -export const getListItemResponseMockWithoutAutoGeneratedValues = (): Partial => ({ - created_by: ELASTIC_USER, +export const getListItemResponseMockWithoutAutoGeneratedValues = ( + elasticUser: string = ELASTIC_USER +): Partial => ({ + created_by: elasticUser, list_id: LIST_ID, type: TYPE, - updated_by: ELASTIC_USER, + updated_by: elasticUser, value: VALUE, }); diff --git a/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts index abc52634e0232..7e6f1647ceb33 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts @@ -44,12 +44,14 @@ export const getListResponseMock = (): ListSchema => ({ * This is useful for end to end tests where we remove the auto generated parts for comparisons * such as created_at, updated_at, and id. */ -export const getListResponseMockWithoutAutoGeneratedValues = (): Partial => ({ - created_by: ELASTIC_USER, +export const getListResponseMockWithoutAutoGeneratedValues = ( + elasticUser: string = ELASTIC_USER +): Partial => ({ + created_by: elasticUser, description: DESCRIPTION, immutable: IMMUTABLE, name: NAME, type: TYPE, - updated_by: ELASTIC_USER, + updated_by: elasticUser, version: VERSION, }); diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx index 7edcf5b53f724..5c2bfb9045688 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx @@ -20,7 +20,7 @@ import { isOperator, matchesOperator, } from '@kbn/securitysolution-list-utils'; -import { validateFilePathInput } from '@kbn/securitysolution-utils'; +import { validatePotentialWildcardInput } from '@kbn/securitysolution-utils'; import { useFindListsBySize } from '@kbn/securitysolution-list-hooks'; import type { FieldSpec } from '@kbn/data-plugin/common'; import { fields, getField } from '@kbn/data-plugin/common/mocks'; @@ -1050,7 +1050,7 @@ describe('BuilderEntryItem', () => { test('it invokes "setWarningsExist" when invalid value in field value input', async () => { const mockSetWarningsExists = jest.fn(); - (validateFilePathInput as jest.Mock).mockReturnValue('some warning message'); + (validatePotentialWildcardInput as jest.Mock).mockReturnValue('some warning message'); wrapper = mount( { test('it does not invoke "setWarningsExist" when valid value in field value input', async () => { const mockSetWarningsExists = jest.fn(); - (validateFilePathInput as jest.Mock).mockReturnValue(undefined); + (validatePotentialWildcardInput as jest.Mock).mockReturnValue(undefined); wrapper = mount( = ({ const renderOperatorInput = (isFirst: boolean): JSX.Element => { // for event filters forms - // show extra operators for wildcards when field is `file.path.text` - const isFilePathTextField = entry.field !== undefined && entry.field.name === 'file.path.text'; + // show extra operators for wildcards when field supports matches + const doesFieldSupportMatches = entry.field !== undefined && fieldSupportsMatches(entry.field); const isEventFilterList = listType === 'endpoint_events'; const augmentedOperatorsList = - operatorsList && isFilePathTextField && isEventFilterList + operatorsList && doesFieldSupportMatches && isEventFilterList ? operatorsList : operatorsList?.filter((operator) => operator.type !== OperatorTypeEnum.WILDCARD); @@ -358,8 +359,8 @@ export const BuilderEntryItem: React.FC = ({ } }; - // show this when wildcard filename with matches operator - const getWildcardWarning = (precedingWarning: string): React.ReactNode => { + // show this when wildcard with matches operator + const getWildcardWarningInfo = (precedingWarning: string): React.ReactNode => { return (

    {precedingWarning}{' '} @@ -368,7 +369,7 @@ export const BuilderEntryItem: React.FC = ({ content={ } size="m" @@ -430,11 +431,13 @@ export const BuilderEntryItem: React.FC = ({ if (osTypes) { [os] = osTypes as OperatingSystem[]; } - const warning = validateFilePathInput({ os, value: wildcardValue }); + const warning = validatePotentialWildcardInput({ + field: entry.field?.name, + os, + value: wildcardValue, + }); actualWarning = - warning === FILENAME_WILDCARD_WARNING - ? warning && getWildcardWarning(warning) - : warning; + warning === WILDCARD_WARNING ? warning && getWildcardWarningInfo(warning) : warning; } return ( diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts index 3cc350629ff96..ef3982166e19b 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts +++ b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts @@ -212,7 +212,7 @@ describe('Exception builder helpers', () => { expect(output).toEqual(expected); }); - test('it returns all fields unfiletered if "item.nested" is not "child" or "parent"', () => { + test('it returns all fields unfiltered if "item.nested" is not "child" or "parent"', () => { const payloadIndexPattern = getMockIndexPattern(); const payloadItem: FormattedBuilderEntry = getMockBuilderEntry(); const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 74d65d73f23b8..e74ee0a4b8130 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -7,7 +7,10 @@ import React from 'react'; import { FlyoutContentActions } from '@kbn/discover-plugin/public'; import { DataTableRecord } from '@kbn/discover-utils/src/types'; +import { AgentIcon, CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; import { useMeasure } from 'react-use/lib'; +import { AgentName } from '@kbn/elastic-agent-utils'; +import { first } from 'lodash'; import { FlyoutDoc } from './types'; import * as constants from '../../../common/constants'; import { HighlightField } from './sub_components/highlight_field'; @@ -47,156 +50,170 @@ export function FlyoutHighlights({ }) { const [ref, dimensions] = useMeasure(); const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); + return ( + {/* Service highlight */} {formattedDoc[constants.SERVICE_NAME_FIELD] && ( + } + label={flyoutServiceLabel} + value={flattenedDoc[constants.SERVICE_NAME_FIELD]} width={fieldWidth} /> )} {formattedDoc[constants.TRACE_ID_FIELD] && ( )} - + {/* Infrastructure highlight */} {formattedDoc[constants.HOST_NAME_FIELD] && ( )} {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( )} {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( )} - + {/* Cloud highlight */} {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( + } + label={flyoutCloudProviderLabel} + value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} width={fieldWidth} /> )} {formattedDoc[constants.CLOUD_REGION_FIELD] && ( )} {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( )} {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( )} {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( )} - + {/* Other highlights */} {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( )} {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( )} {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( )} {formattedDoc[constants.AGENT_NAME_FIELD] && ( )} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx index ca47b10548236..1034a3015cc89 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiText, copyToClipboard } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText, copyToClipboard, EuiTextTruncate } from '@elastic/eui'; import React, { ReactNode, useMemo, useState } from 'react'; import { HoverAction, HoverActionType } from './hover_action'; import { @@ -18,21 +18,22 @@ import { import { useDiscoverActionsContext } from '../../../hooks/use_discover_action'; interface HighlightFieldProps { - label: string | ReactNode; field: string; - value: unknown; formattedValue: string; - dataTestSubj: string; + icon?: ReactNode; + label: string | ReactNode; + value: unknown; width: number; } export function HighlightField({ - label, field, - value, formattedValue, - dataTestSubj, + icon, + label, + value, width, + ...props }: HighlightFieldProps) { const filterForText = flyoutHoverActionFilterForText(value); const filterOutText = flyoutHoverActionFilterOutText(value); @@ -89,14 +90,33 @@ export function HighlightField({ [filterForText, filterOutText, actions, field, value, columnAdded] ); return formattedValue ? ( - + {label} - + + + {icon && {icon}} + + + {(truncatedText: string) => ( + + )} + + + + ) : null; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx index 5ed25be2b36d9..1b5883ed082e5 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx @@ -7,14 +7,7 @@ import React from 'react'; -import { - EuiFlexGroup, - EuiToolTip, - EuiButtonIcon, - useEuiTheme, - EuiTextTruncate, - EuiText, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiToolTip, EuiButtonIcon, useEuiTheme, EuiFlexItem } from '@elastic/eui'; import type { IconType } from '@elastic/eui'; export interface HoverActionType { @@ -26,12 +19,11 @@ export interface HoverActionType { } interface HoverActionProps { - displayText: string; + children: React.ReactNode; actions: HoverActionType[]; - width: number; } -export const HoverAction = ({ displayText, actions, width }: HoverActionProps) => { +export const HoverAction = ({ children, actions }: HoverActionProps) => { const { euiTheme } = useEuiTheme(); return ( @@ -49,14 +41,7 @@ export const HoverAction = ({ displayText, actions, width }: HoverActionProps) = }, }} > - - {(truncatedText: string) => ( - - )} - + {children} { {aiAssistant.isEnabled() && explainLogMessageMessages ? ( - + ) : null} {aiAssistant.isEnabled() && similarLogMessageMessages ? ( - + ) : null} diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 06c84a7d06267..456748a3752f1 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -342,3 +342,6 @@ export enum MASK_OPERATOR { // Maplibre does not provide any feedback when rendering is complete. // Workaround is hard-coded timeout period. export const RENDER_TIMEOUT = 1000; + +export const MIDDLE_TRUNCATION_PROPS = { truncation: 'middle' as const }; +export const SINGLE_SELECTION_AS_TEXT_PROPS = { asPlainText: true }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 7e23353460df6..105b17dcd5a21 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -27,6 +27,13 @@ import { LICENSED_FEATURES } from '../../../licensed_features'; jest.mock('../../../kibana_services'); export class MockSearchSource { + getField(fieldName: string) { + if (fieldName === 'filter') { + return []; + } + + throw new Error(`Unsupported search source field: ${fieldName}`); + } setField = jest.fn(); setParent() {} getSearchRequestBody() { @@ -324,7 +331,7 @@ describe('ESGeoGridSource', () => { index: 'foo-*', renderAs: 'heatmap', requestBody: - "(fields:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'')),'6':('0':aggs,'1':())))", + "(fields:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'')),'6':('0':aggs,'1':()),'7':('0':filter,'1':!((meta:(),query:(exists:(field:bar)))))))", token: '1234', }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx index 4acb400807ad0..703ab4f45b962 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx @@ -21,6 +21,7 @@ import { DataView } from '@kbn/data-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { ACTION_GLOBAL_APPLY_FILTER } from '@kbn/unified-search-plugin/public'; import { getTileUrlParams } from '@kbn/maps-vector-tile-utils'; +import { type Filter, buildExistsFilter } from '@kbn/es-query'; import { makeESBbox } from '../../../../common/elasticsearch_util'; import { convertCompositeRespToGeoJson, convertRegularRespToGeoJson } from './convert_to_geojson'; import { UpdateSourceEditor } from './update_source_editor'; @@ -553,6 +554,11 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo const dataView = await this.getIndexPattern(); const searchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('aggs', this.getValueAggsDsl(dataView)); + // Filter out documents without geo fields for broad index-pattern support + searchSource.setField('filter', [ + ...(searchSource.getField('filter') as Filter[]), + buildExistsFilter({ name: this._descriptor.geoField, type: 'geo_point' }, dataView), + ]); const mvtUrlServicePath = getHttp().basePath.prepend( `${MVT_GETGRIDTILE_API_PATH}/{z}/{x}/{y}.pbf` diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts index 7f7fde393de91..3585150459b89 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts @@ -53,6 +53,13 @@ describe('ESSearchSource', () => { beforeEach(async () => { const mockSearchSource = { + getField: (fieldName: string) => { + if (fieldName === 'filter') { + return []; + } + + throw new Error(`Unsupported search source field: ${fieldName}`); + }, setField: jest.fn(), getSearchRequestBody() { return { @@ -131,7 +138,7 @@ describe('ESSearchSource', () => { hasLabels: 'false', index: 'foobar-title-*', requestBody: - "(fields:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar')),'6':('0':fieldsFromSource,'1':!(_id)),'7':('0':source,'1':!f),'8':('0':fields,'1':!(tooltipField,styleField))))", + "(fields:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar')),'6':('0':fieldsFromSource,'1':!(_id)),'7':('0':source,'1':!f),'8':('0':fields,'1':!(tooltipField,styleField)),'9':('0':filter,'1':!((meta:(),query:(exists:(field:bar)))))))", token: '1234', }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 77f4b684caf1f..5d0f6aa59c55d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import type { SearchResponseWarning } from '@kbn/search-response-warnings'; import { GeoJsonProperties, Geometry, Position } from 'geojson'; import type { KibanaExecutionContext } from '@kbn/core/public'; -import { type Filter, buildPhraseFilter, type TimeRange } from '@kbn/es-query'; +import { type Filter, buildExistsFilter, buildPhraseFilter, type TimeRange } from '@kbn/es-query'; import type { DataViewField, DataView } from '@kbn/data-plugin/common'; import { lastValueFrom } from 'rxjs'; import { Adapters } from '@kbn/inspector-plugin/common/adapters'; @@ -923,6 +923,12 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource }) ); + // Filter out documents without geo fields to avoid shard failures for indices without geo fields + searchSource.setField('filter', [ + ...(searchSource.getField('filter') as Filter[]), + buildExistsFilter({ name: this._descriptor.geoField, type: 'geo_point' }, dataView), + ]); + const mvtUrlServicePath = getHttp().basePath.prepend(`${MVT_GETTILE_API_PATH}/{z}/{x}/{y}.pbf`); const tileUrlParams = getTileUrlParams({ diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/field_select.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/field_select.tsx index 3de0292292b41..a2c5f69e55e68 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/field_select.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/field_select.tsx @@ -6,36 +6,18 @@ */ import React from 'react'; - -import { - EuiComboBox, - EuiComboBoxProps, - EuiComboBoxOptionOption, - EuiHighlight, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; +import { calculateWidthFromEntries } from '@kbn/calculate-width-from-char-count'; +import { EuiComboBox, EuiComboBoxProps, EuiComboBoxOptionOption } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FieldIcon } from '@kbn/react-field'; -import { FIELD_ORIGIN, VECTOR_STYLES } from '../../../../../common/constants'; +import { + FIELD_ORIGIN, + MIDDLE_TRUNCATION_PROPS, + SINGLE_SELECTION_AS_TEXT_PROPS, + VECTOR_STYLES, +} from '../../../../../common/constants'; import { StyleField } from '../style_fields_helper'; -function renderOption( - option: EuiComboBoxOptionOption, - searchValue: string, - contentClassName: string -) { - const fieldIcon = option.value ? : null; - return ( - - {fieldIcon} - - {option.label} - - - ); -} - function groupFieldsByOrigin(fields: StyleField[]) { const fieldsByOriginMap = new Map(); fields.forEach((field) => { @@ -56,6 +38,9 @@ function groupFieldsByOrigin(fields: StyleField[]) { label: field.label, disabled: field.isUnsupported, title: field.unsupportedMsg, + prepend: field.type ? ( + + ) : null, }; }) .sort((a, b) => { @@ -129,19 +114,24 @@ export function FieldSelect({ fields, selectedFieldName, onChange, styleName, .. } } + const options = groupFieldsByOrigin(fields); + + const panelMinWidth = calculateWidthFromEntries(fields, ['label']); + return ( ); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/stop_input.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/stop_input.tsx index ad7497d97a6e5..d695e605d7b8c 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/stop_input.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/stop_input.tsx @@ -8,6 +8,7 @@ import _ from 'lodash'; import React, { ChangeEvent, Component } from 'react'; import { EuiComboBox, EuiComboBoxOptionOption, EuiFieldText } from '@elastic/eui'; +import { SINGLE_SELECTION_AS_TEXT_PROPS } from '../../../../../common/constants'; import { IField } from '../../../fields/field'; interface Props { @@ -132,7 +133,7 @@ export class StopInput extends Component { boolean + isFieldDisabled?: (field: DataViewField) => boolean, + getFieldDisabledReason?: (field: DataViewField) => string | null ): Array> { if (!fields) { return []; @@ -30,12 +25,43 @@ function fieldsToOptions( return fields .map((field) => { + const FieldTypeIcon = field.type ? ( + + ) : null; const option: EuiComboBoxOptionOption = { value: field, label: field.displayName ? field.displayName : field.name, + prepend: FieldTypeIcon, }; if (isFieldDisabled && isFieldDisabled(field)) { option.disabled = true; + + const disabledReason = + option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null; + + if (disabledReason) { + option.prepend = ( + <> + {FieldTypeIcon} + +

    + + + ); + } } return option; }) @@ -63,34 +89,6 @@ export function SingleFieldSelect({ value, ...rest }: Props) { - function renderOption( - option: EuiComboBoxOptionOption, - searchValue: string, - contentClassName: string - ) { - const content = ( - - - - - - {option.label} - - - ); - - const disabledReason = - option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null; - - return disabledReason ? ( - - {content} - - ) : ( - content - ); - } - const onSelection = (selectedOptions: Array>) => { onChange(_.get(selectedOptions, '0.value.name')); }; @@ -108,14 +106,19 @@ export function SingleFieldSelect({ } } + const options = fieldsToOptions(fields, isFieldDisabled, getFieldDisabledReason); + + const panelMinWidth = calculateWidthFromEntries(options, ['label']); + return ( ); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_error_cache.ts b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_error_cache.ts index 9d54457056f77..213f883762312 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_error_cache.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_error_cache.ts @@ -18,6 +18,15 @@ export function getErrorCacheTileKey(canonical: { x: number; y: number; z: numbe export class TileErrorCache { private _cache: Record> = {}; + public clearLayer(layerId: string, onClear: () => void) { + if (!(layerId in this._cache)) { + return; + } + + delete this._cache[layerId]; + onClear(); + } + public clearTileError(layerId: string | undefined, tileKey: string, onClear: () => void) { if (!layerId || !(layerId in this._cache)) { return; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.test.tsx index 546001cc23b3b..c8d6395c743ba 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.test.tsx @@ -12,6 +12,7 @@ import type { Map as MbMap, MapSourceDataEvent } from '@kbn/mapbox-gl'; import type { TileError, TileMetaFeature } from '../../../../common/descriptor_types'; import { TileStatusTracker } from './tile_status_tracker'; import { ILayer } from '../../../classes/layers/layer'; +import type { IVectorSource } from '../../../classes/sources/vector_source'; class MockMbMap { public listeners: Array<{ type: string; callback: (e: unknown) => void }> = []; @@ -249,6 +250,53 @@ describe('TileStatusTracker', () => { expect(tileErrorsMap.get('layer2')).toBeUndefined(); }); + test('should clear layer tile errors when layer is not tiled', async () => { + const mockMbMap = new MockMbMap(); + const layer1 = createMockLayer('layer1', 'layer1Source'); + + const wrapper = mount( + + ); + + mockMbMap.emit( + 'sourcedataloading', + createSourceDataEvent('layer1Source', IN_VIEW_CANONICAL_TILE) + ); + mockMbMap.emit('error', { + ...createSourceDataEvent('layer1Source', IN_VIEW_CANONICAL_TILE), + error: { + message: 'simulated error', + }, + }); + + // simulate delay. Cache-checking is debounced. + await sleep(300); + + expect(tileErrorsMap.get('layer1')?.length).toBe(1); + + const geojsonLayer1 = createMockLayer('layer1', 'layer1Source'); + geojsonLayer1.getSource = () => { + return { + isESSource() { + return true; + }, + isMvt() { + return false; + }, + } as unknown as IVectorSource; + }; + wrapper.setProps({ layerList: [geojsonLayer1] }); + + // simulate delay. Cache-checking is debounced. + await sleep(300); + + expect(tileErrorsMap.get('layer1')).toBeUndefined(); + }); + test('should only return tile errors within map zoom', async () => { const mockMbMap = new MockMbMap(); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.tsx index ccb1ca6d06c5d..972e691f6695e 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.tsx @@ -12,6 +12,7 @@ import type { AJAXError, Map as MbMap, MapSourceDataEvent } from '@kbn/mapbox-gl import type { TileError, TileMetaFeature } from '../../../../common/descriptor_types'; import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants'; import { ILayer } from '../../../classes/layers/layer'; +import { isLayerGroup } from '../../../classes/layers/layer_group'; import { IVectorSource } from '../../../classes/sources/vector_source'; import { getTileKey as getCenterTileKey } from '../../../classes/util/geo_tile_utils'; import { boundsToExtent } from '../../../classes/util/maplibre_utils'; @@ -62,6 +63,24 @@ export class TileStatusTracker extends Component { this.props.mbMap.on('moveend', this._onMoveEnd); } + componentDidUpdate() { + this.props.layerList.forEach((layer) => { + if (isLayerGroup(layer)) { + return; + } + + const source = layer.getSource(); + if ( + source.isESSource() && + typeof (source as IVectorSource).isMvt === 'function' && + !(source as IVectorSource).isMvt() + ) { + // clear tile cache when layer is not tiled + this._tileErrorCache.clearLayer(layer.getId(), this._updateTileStatusForAllLayers); + } + }); + } + componentWillUnmount() { this._isMounted = false; this.props.mbMap.off('error', this._onError); diff --git a/x-pack/plugins/maps/public/maps_vis_type_alias.ts b/x-pack/plugins/maps/public/maps_vis_type_alias.ts index bcbf0170afb25..e161689643612 100644 --- a/x-pack/plugins/maps/public/maps_vis_type_alias.ts +++ b/x-pack/plugins/maps/public/maps_vis_type_alias.ts @@ -24,8 +24,10 @@ export function getMapsVisTypeAlias() { }); return { - aliasApp: APP_ID, - aliasPath: `/${MAP_PATH}`, + alias: { + app: APP_ID, + path: `/${MAP_PATH}`, + }, name: APP_ID, title: APP_NAME, description: appDescription, @@ -45,8 +47,10 @@ export function getMapsVisTypeAlias() { title, description, updatedAt, - editUrl: getEditPath(id), - editApp: APP_ID, + editor: { + editUrl: getEditPath(id), + editApp: APP_ID, + }, icon: APP_ICON, stage: 'production' as VisualizationStage, savedObjectType: type, diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 86955eeea584a..2380d7c3a9425 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -75,6 +75,7 @@ "@kbn/serverless", "@kbn/logging", "@kbn/search-response-warnings", + "@kbn/calculate-width-from-char-count", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/metrics_data_access/common/index.ts b/x-pack/plugins/metrics_data_access/common/index.ts index 38970225a8644..a236ceeac16f0 100644 --- a/x-pack/plugins/metrics_data_access/common/index.ts +++ b/x-pack/plugins/metrics_data_access/common/index.ts @@ -13,7 +13,7 @@ export { metrics, } from './inventory_models'; -export { podSnapshotMetricTypes } from './inventory_models/pod'; +export { podSnapshotMetricTypes } from './inventory_models/kubernetes/pod'; export { containerSnapshotMetricTypes } from './inventory_models/container'; export { awsS3SnapshotMetricTypes } from './inventory_models/aws_s3'; export { hostSnapshotMetricTypes } from './inventory_models/host'; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/create_dashboard_model.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/create_dashboard_model.ts new file mode 100644 index 0000000000000..8fc63d9a91a2b --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/create_dashboard_model.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 { DashboardModel } from './types'; + +export const createDashboardModel = ({ + charts, + dependsOn = [], +}: DashboardModel): DashboardModel => { + return { + dependsOn, + charts, + }; +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/index.ts index 0b7389b74537d..e1d4f60bc9f74 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/host/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/index.ts @@ -12,10 +12,9 @@ import { aws as awsRequiredMetrics, nginx as nginxRequireMetrics, } from '../shared/metrics/required_metrics'; - export { hostSnapshotMetricTypes } from './metrics'; -export const host: InventoryModel = { +export const host: InventoryModel = { id: 'host', displayName: i18n.translate('xpack.metricsData.inventoryModel.host.displayName', { defaultMessage: 'Hosts', diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/cpu_charts.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/cpu_charts.ts new file mode 100644 index 0000000000000..307db0766accf --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/cpu_charts.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { StaticValueConfig, XYChartModel } from '@kbn/lens-embeddable-utils'; +import { formulas } from '../formulas'; +import type { ChartArgs } from './types'; + +export const REFERENCE_LINE: StaticValueConfig = { + value: '1', + format: { + id: 'percent', + params: { + decimals: 0, + }, + }, + color: '#6092c0', +}; + +export const cpuUsageBreakdown = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'cpuUsageBreakdown', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.cpuUsage', { + defaultMessage: 'CPU Usage', + }), + layers: [ + { + data: [ + formulas.cpuUsageIowait, + formulas.cpuUsageIrq, + formulas.cpuUsageNice, + formulas.cpuUsageSoftirq, + formulas.cpuUsageSteal, + formulas.cpuUsageUser, + formulas.cpuUsageSystem, + ], + options: { + seriesType: 'area_stacked', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView, + }), +}; + +export const normalizedLoad1m = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'normalizedLoad1m', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.normalizedLoad1m', { + defaultMessage: 'Normalized Load', + }), + layers: [ + { data: [formulas.normalizedLoad1m], layerType: 'data' }, + { data: [REFERENCE_LINE], layerType: 'referenceLine' }, + ], + visualizationType: 'lnsXY', + dataView, + }), +}; + +export const loadBreakdown = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'loadBreakdown', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.load', { + defaultMessage: 'Load', + }), + layers: [ + { + data: [formulas.load1m, formulas.load5m, formulas.load15m], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView, + }), +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk_charts.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk_charts.ts new file mode 100644 index 0000000000000..53f2c3e8ea505 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk_charts.ts @@ -0,0 +1,192 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { XYChartModel } from '@kbn/lens-embeddable-utils'; +import { formulas } from '../formulas'; +import type { ChartArgs } from './types'; + +const TOP_VALUES_SIZE = 5; + +export const diskSpaceUsageAvailable = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'diskSpaceUsageAvailable', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskUsage', { + defaultMessage: 'Disk Usage', + }), + layers: [ + { + data: [ + { + ...formulas.diskUsage, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used', + { + defaultMessage: 'Used', + } + ), + }, + { + ...formulas.diskSpaceAvailability, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.available', + { + defaultMessage: 'Available', + } + ), + }, + ], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView, + }), +}; + +export const diskUsageByMountPoint = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'DiskUsageByMountPoint', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint', { + defaultMessage: 'Disk Usage by Mount Point', + }), + layers: [ + { + data: [ + { + ...formulas.diskUsage, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used', + { + defaultMessage: 'Used', + } + ), + }, + ], + options: { + seriesType: 'area', + breakdown: { + type: 'top_values', + field: 'system.filesystem.mount_point', + params: { + size: TOP_VALUES_SIZE, + }, + }, + }, + layerType: 'data', + }, + ], + visualOptions: { + legend: { + isVisible: true, + position: 'bottom', + legendSize: 50 as any, + }, + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + visualizationType: 'lnsXY', + dataView, + }), +}; +export const diskThroughputReadWrite = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'diskThroughputReadWrite', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskIOPS', { + defaultMessage: 'Disk IOPS', + }), + layers: [ + { + data: [ + { + ...formulas.diskIORead, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.read', + { + defaultMessage: 'Read', + } + ), + }, + { + ...formulas.diskIOWrite, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.write', + { + defaultMessage: 'Write', + } + ), + }, + ], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualOptions: { + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + visualizationType: 'lnsXY', + dataView, + }), +}; + +export const diskIOReadWrite = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'diskIOReadWrite', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskThroughput', { + defaultMessage: 'Disk Throughput', + }), + layers: [ + { + data: [ + { + ...formulas.diskReadThroughput, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.read', + { + defaultMessage: 'Read', + } + ), + }, + { + ...formulas.diskWriteThroughput, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.write', + { + defaultMessage: 'Write', + } + ), + }, + ], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualOptions: { + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + visualizationType: 'lnsXY', + dataView, + }), +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/index.ts new file mode 100644 index 0000000000000..1f0a3947e4617 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + type ChartModel, + type XYChartModel, + type MetricChartModel, + type XYLayerOptions, + type MetricLayerOptions, + type ChartTypes, + type XYVisualOptions, + XY_ID, +} from '@kbn/lens-embeddable-utils'; +import type { HostFormulaNames } from '../formulas'; +import { formulas } from '../formulas'; + +type ChartByFormula = Record< + HostFormulaNames, + TType extends typeof XY_ID ? XYChartModel : MetricChartModel +>; + +type BaseArgs = Pick & { + formulaIds: HostFormulaNames[]; + visualizationType: TType; + layerOptions?: TType extends typeof XY_ID ? XYLayerOptions : MetricLayerOptions; + visualOptions?: TType extends typeof XY_ID ? XYVisualOptions : never; +}; + +export const createBasicCharts = ({ + formulaIds, + visualizationType, + dataView, + layerOptions, + ...rest +}: BaseArgs): ChartByFormula => { + return formulaIds.reduce((acc, curr) => { + const layers = { + data: visualizationType === XY_ID ? [formulas[curr]] : formulas[curr], + layerType: visualizationType === XY_ID ? 'data' : 'metricTrendline', + options: layerOptions, + }; + + const chartModel = { + id: curr, + title: formulas[curr].label, + dataView, + visualizationType, + layers: visualizationType === XY_ID ? [layers] : layers, + ...rest, + } as TType extends typeof XY_ID ? XYChartModel : MetricChartModel; + + return { + ...acc, + [curr]: chartModel, + }; + }, {} as ChartByFormula); +}; + +// custom charts +export { cpuUsageBreakdown, normalizedLoad1m, loadBreakdown } from './cpu_charts'; +export { + diskIOReadWrite, + diskSpaceUsageAvailable, + diskThroughputReadWrite, + diskUsageByMountPoint, +} from './disk_charts'; +export { memoryUsageBreakdown } from './memory_charts'; +export { rxTx } from './network_charts'; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/memory_charts.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/memory_charts.ts new file mode 100644 index 0000000000000..95c30fcdb18e6 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/memory_charts.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { XYChartModel } from '@kbn/lens-embeddable-utils'; +import { formulas } from '../formulas'; +import type { ChartArgs } from './types'; + +export const memoryUsageBreakdown = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'memoryUsageBreakdown', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.memoryUsage', { + defaultMessage: 'Memory Usage', + }), + layers: [ + { + data: [ + { + ...formulas.memoryCache, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.cache', + { + defaultMessage: 'Cache', + } + ), + }, + { + ...formulas.memoryUsed, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.used', + { + defaultMessage: 'Used', + } + ), + }, + { + ...formulas.memoryFreeExcludingCache, + label: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.metric.label.free', + { + defaultMessage: 'Free', + } + ), + }, + ], + options: { + seriesType: 'area_stacked', + }, + layerType: 'data', + }, + ], + visualOptions: { + legend: { + isVisible: true, + position: 'bottom', + legendSize: 50 as any, + }, + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + visualizationType: 'lnsXY', + dataView, + }), +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/network_charts.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/network_charts.ts new file mode 100644 index 0000000000000..5181b5b075765 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/network_charts.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 { i18n } from '@kbn/i18n'; +import type { XYChartModel } from '@kbn/lens-embeddable-utils'; +import { formulas } from '../formulas'; +import type { ChartArgs } from './types'; + +export const rxTx = { + get: ({ dataView }: ChartArgs): XYChartModel => ({ + id: 'rxTx', + title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.network', { + defaultMessage: 'Network', + }), + layers: [ + { + data: [ + { + ...formulas.rx, + label: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.network.label.rx', { + defaultMessage: 'Inbound (RX)', + }), + }, + { + ...formulas.tx, + label: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.network.label.tx', { + defaultMessage: 'Outbound (TX)', + }), + }, + ], + options: { + seriesType: 'area', + }, + + layerType: 'data', + }, + ], + visualOptions: { + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + visualizationType: 'lnsXY', + dataView, + }), +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/types.ts similarity index 68% rename from x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/index.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/types.ts index 73d468f206eb8..f3634648b4fdb 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/dashboards/hosts_view/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/types.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { hostsMetricCharts } from './hosts_metric_charts'; +import { DataView } from '@kbn/data-views-plugin/common'; -export const hostsViewDashboards = { - hostsMetricCharts, -}; +export interface ChartArgs { + dataView?: DataView; +} diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details.ts new file mode 100644 index 0000000000000..c6fc7c1cdc9aa --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details.ts @@ -0,0 +1,97 @@ +/* + * Copyright 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 { DataView } from '@kbn/data-views-plugin/common'; +import type { XYVisualOptions } from '@kbn/lens-embeddable-utils'; +import { createDashboardModel } from '../../../create_dashboard_model'; +import { + createBasicCharts, + cpuUsageBreakdown, + diskSpaceUsageAvailable, + diskUsageByMountPoint, + diskIOReadWrite, + diskThroughputReadWrite, + memoryUsageBreakdown, + loadBreakdown, + rxTx, +} from '../charts'; + +export const assetDetails = { + get: ({ + metricsDataView, + logsDataView, + }: { + metricsDataView?: DataView; + logsDataView?: DataView; + }) => { + const commonVisualOptions: XYVisualOptions = { + showDottedLine: true, + missingValues: 'Linear', + }; + + const legend: XYVisualOptions = { + legend: { + isVisible: true, + position: 'bottom', + }, + }; + + const { cpuUsage, memoryUsage, normalizedLoad1m } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: ['cpuUsage', 'memoryUsage', 'normalizedLoad1m'], + dataView: metricsDataView, + visualOptions: commonVisualOptions, + }); + + const { logRate } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: ['logRate'], + dataView: logsDataView, + visualOptions: commonVisualOptions, + }); + + return createDashboardModel({ + charts: [ + cpuUsage, + { + ...cpuUsageBreakdown.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + memoryUsage, + { + ...memoryUsageBreakdown.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + normalizedLoad1m, + { + ...loadBreakdown.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + logRate, + { + ...diskSpaceUsageAvailable.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskUsageByMountPoint.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskThroughputReadWrite.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskIOReadWrite.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...rxTx.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + ], + }); + }, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_flyout.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_flyout.ts new file mode 100644 index 0000000000000..96436e3d00f41 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_flyout.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { DataView } from '@kbn/data-views-plugin/common'; +import type { XYVisualOptions } from '@kbn/lens-embeddable-utils'; +import { createDashboardModel } from '../../../create_dashboard_model'; +import { + createBasicCharts, + diskSpaceUsageAvailable, + diskUsageByMountPoint, + diskIOReadWrite, + diskThroughputReadWrite, + rxTx, +} from '../charts'; + +export const assetDetailsFlyout = { + get: ({ + metricsDataView, + logsDataView, + }: { + metricsDataView?: DataView; + logsDataView?: DataView; + }) => { + const commonVisualOptions: XYVisualOptions = { + showDottedLine: true, + missingValues: 'Linear', + }; + + const legend: XYVisualOptions = { + legend: { + isVisible: true, + position: 'bottom', + }, + }; + + const { cpuUsage, memoryUsage, normalizedLoad1m } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: ['cpuUsage', 'memoryUsage', 'normalizedLoad1m'], + dataView: metricsDataView, + visualOptions: commonVisualOptions, + }); + + const { logRate } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: ['logRate'], + dataView: logsDataView, + visualOptions: commonVisualOptions, + }); + + return createDashboardModel({ + charts: [ + cpuUsage, + memoryUsage, + normalizedLoad1m, + logRate, + { + ...diskSpaceUsageAvailable.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskUsageByMountPoint.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskThroughputReadWrite.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...diskIOReadWrite.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + { + ...rxTx.get({ dataView: metricsDataView }), + visualOptions: { ...commonVisualOptions, ...legend }, + }, + ], + }); + }, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_kubernetes_node.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_kubernetes_node.ts new file mode 100644 index 0000000000000..cd9dd1591f35e --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/asset_details_kubernetes_node.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { DataView } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import type { XYVisualOptions } from '@kbn/lens-embeddable-utils'; +import { createDashboardModel } from '../../../create_dashboard_model'; +import { formulas } from '../../../kubernetes/node/metrics'; + +export const assetDetailsKubernetesNode = { + get: ({ metricsDataView }: { metricsDataView?: DataView }) => { + const commonVisualOptions: XYVisualOptions = { + showDottedLine: true, + missingValues: 'Linear', + legend: { + isVisible: true, + position: 'bottom', + }, + }; + + return createDashboardModel({ + dependsOn: ['kubernetes.node'], + charts: [ + { + id: 'nodeCpuCapacity', + title: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity', + { + defaultMessage: 'Node CPU Capacity', + } + ), + layers: [ + { + data: [formulas.nodeCpuCapacity, formulas.nodeCpuUsed], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + dataView: metricsDataView, + visualizationType: 'lnsXY', + visualOptions: commonVisualOptions, + }, + { + id: 'nodeMemoryCapacity', + title: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity', + { + defaultMessage: 'Node Memory Capacity', + } + ), + layers: [ + { + data: [formulas.nodeMemoryCapacity, formulas.nodeMemoryUsed], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView: metricsDataView, + visualOptions: commonVisualOptions, + }, + { + id: 'nodeDiskCapacity', + title: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity', + { + defaultMessage: 'Node Disk Capacity', + } + ), + layers: [ + { + data: [formulas.nodeDiskCapacity, formulas.nodeDiskUsed], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView: metricsDataView, + visualOptions: commonVisualOptions, + }, + { + id: 'nodePodCapacity', + title: i18n.translate( + 'xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity', + { + defaultMessage: 'Node Pod Capacity', + } + ), + layers: [ + { + data: [formulas.nodePodCapacity, formulas.nodePodUsed], + options: { + seriesType: 'area', + }, + layerType: 'data', + }, + ], + visualizationType: 'lnsXY', + dataView: metricsDataView, + visualOptions: commonVisualOptions, + }, + ], + }); + }, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/hosts_view.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/hosts_view.ts new file mode 100644 index 0000000000000..8ba14603e85a8 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/hosts_view.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataView } from '@kbn/data-views-plugin/common'; +import { XYChartModel, XYLayerOptions } from '@kbn/lens-embeddable-utils'; +import { createDashboardModel } from '../../../create_dashboard_model'; +import { createBasicCharts } from '../charts'; + +export const hostsView = { + get: ({ metricsDataView }: { metricsDataView?: DataView }) => { + const commonVisualOptions: XYChartModel['visualOptions'] = { + showDottedLine: true, + missingValues: 'Linear', + }; + + const layerOptions: XYLayerOptions = { + breakdown: { + type: 'top_values', + field: 'host.name', + params: { + size: 20, + }, + }, + }; + + const { + memoryUsage, + memoryFree, + diskUsage, + diskSpaceAvailable, + diskIORead, + diskIOWrite, + diskReadThroughput, + diskWriteThroughput, + rx, + tx, + } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: [ + 'cpuUsage', + 'memoryUsage', + 'normalizedLoad1m', + 'memoryFree', + 'diskUsage', + 'diskSpaceAvailable', + 'diskIORead', + 'diskIOWrite', + 'diskReadThroughput', + 'diskWriteThroughput', + 'rx', + 'tx', + ], + dataView: metricsDataView, + layerOptions, + visualOptions: commonVisualOptions, + }); + + const { cpuUsage, normalizedLoad1m } = createBasicCharts({ + visualizationType: 'lnsXY', + formulaIds: ['cpuUsage', 'normalizedLoad1m'], + layerOptions, + visualOptions: { + ...commonVisualOptions, + yLeftExtent: { + mode: 'dataBounds', + lowerBound: 0, + upperBound: 1, + }, + }, + dataView: metricsDataView, + }); + + return createDashboardModel({ + charts: [ + cpuUsage, + normalizedLoad1m, + memoryUsage, + memoryFree, + diskUsage, + diskSpaceAvailable, + diskIORead, + diskIOWrite, + diskReadThroughput, + diskWriteThroughput, + rx, + tx, + ], + }); + }, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/index.ts new file mode 100644 index 0000000000000..99cb745fa5ed2 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/index.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 { assetDetails } from './asset_details'; +import { assetDetailsFlyout } from './asset_details_flyout'; +import { hostsView } from './hosts_view'; +import { kpi } from './kpi'; +import { assetDetailsKubernetesNode } from './asset_details_kubernetes_node'; + +export const dashboards = { + assetDetails, + assetDetailsFlyout, + hostsView, + kpi, + assetDetailsKubernetesNode, +}; + +export type HostDashboards = typeof dashboards; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/kpi.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/kpi.ts new file mode 100644 index 0000000000000..28a929f147e71 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/dashboards/kpi.ts @@ -0,0 +1,110 @@ +/* + * Copyright 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 { DataView } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import type { MetricLayerOptions } from '@kbn/lens-embeddable-utils'; +import { createDashboardModel } from '../../../create_dashboard_model'; +import { createBasicCharts } from '../charts'; + +const AVERAGE = i18n.translate('xpack.metricsData.assetDetails.overview.kpi.subtitle.average', { + defaultMessage: 'Average', +}); + +export const kpi = { + get: ({ + metricsDataView, + options, + }: { + metricsDataView?: DataView; + options?: MetricLayerOptions; + }) => { + const { cpuUsage, diskUsage, memoryUsage, normalizedLoad1m } = createBasicCharts({ + visualizationType: 'lnsMetric', + formulaIds: ['cpuUsage', 'diskUsage', 'memoryUsage', 'normalizedLoad1m'], + layerOptions: { + showTrendLine: true, + subtitle: AVERAGE, + ...options, + }, + dataView: metricsDataView, + }); + + return createDashboardModel({ + charts: [ + { + ...cpuUsage, + layers: { + ...cpuUsage.layers, + data: { + ...cpuUsage.layers.data, + format: cpuUsage.layers.data.format + ? { + ...cpuUsage.layers.data.format, + params: { + decimals: 1, + }, + } + : undefined, + }, + }, + }, + { + ...normalizedLoad1m, + layers: { + ...normalizedLoad1m.layers, + data: { + ...normalizedLoad1m.layers.data, + format: normalizedLoad1m.layers.data.format + ? { + ...normalizedLoad1m.layers.data.format, + params: { + decimals: 1, + }, + } + : undefined, + }, + }, + }, + + { + ...memoryUsage, + layers: { + ...memoryUsage.layers, + data: { + ...memoryUsage.layers.data, + format: memoryUsage.layers.data.format + ? { + ...memoryUsage.layers.data.format, + params: { + decimals: 1, + }, + } + : undefined, + }, + }, + }, + { + ...diskUsage, + layers: { + ...diskUsage.layers, + data: { + ...diskUsage.layers.data, + format: diskUsage.layers.data.format + ? { + ...diskUsage.layers.data.format, + params: { + decimals: 1, + }, + } + : undefined, + }, + }, + }, + ], + }); + }, +}; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage.ts index f98c02643dc2e..5fc722ef8ee7f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsage: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage', { defaultMessage: 'CPU Usage', }), value: '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_iowait.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_iowait.ts index b43f77252dcc1..884306852f87d 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_iowait.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_iowait.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageIowait: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.iowaitLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel', { defaultMessage: 'iowait', }), value: 'average(system.cpu.iowait.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_irq.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_irq.ts index f5fb7f2319769..148939a0ffd3e 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_irq.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_irq.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageIrq: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.irqLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel', { defaultMessage: 'irq', }), value: 'average(system.cpu.irq.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_nice.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_nice.ts index 94093af85a90d..24d2400d0a2ca 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_nice.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_nice.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageNice: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.niceLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel', { defaultMessage: 'nice', }), value: 'average(system.cpu.nice.norm.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_softirq.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_softirq.ts index adc9428a24947..8ec868af679a8 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_softirq.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_softirq.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageSoftirq: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.softirqLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.softirqLabel', { defaultMessage: 'softirq', }), value: 'average(system.cpu.softirq.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_steal.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_steal.ts index 4ca7ffb7c3352..a400551d492e1 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_steal.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_steal.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageSteal: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.stealLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel', { defaultMessage: 'steal', }), value: 'average(system.cpu.steal.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_system.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_system.ts index 591734973647b..80e2e369f3f20 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_system.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_system.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageSystem: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.systemLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel', { defaultMessage: 'system', }), value: 'average(system.cpu.system.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_user.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_user.ts index 8fe8d2baf1f3b..6fb45344f8896 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/cpu_usage_user.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu_usage_user.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const cpuUsageUser: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.cpuUsage.userLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel', { defaultMessage: 'user', }), value: 'average(system.cpu.user.pct) / max(system.cpu.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_iops.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_iops.ts index 74f3b6b27b2e2..a21a735856ded 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_iops.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_iops.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskIORead: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskIORead', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskIORead', { defaultMessage: 'Disk Read IOPS', }), value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_throughput.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_throughput.ts index eb92ba34406e3..73b684dc5a65b 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_read_throughput.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_read_throughput.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskReadThroughput: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskReadThroughput', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskReadThroughput', { defaultMessage: 'Disk Read Throughput', }), value: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes: *')", diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_availability.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_availability.ts index 02fc69d72b106..6ea6811d30d0f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_availability.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_availability.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskSpaceAvailability: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskSpaceAvailability', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskSpaceAvailability', { defaultMessage: 'Disk Space Availability', }), value: '1 - average(system.filesystem.used.pct)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_available.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_available.ts index f8d34c24ffee0..c6e9919b4c532 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_space_available.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_space_available.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskSpaceAvailable: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskSpaceAvailable', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskSpaceAvailable', { defaultMessage: 'Disk Space Available', }), value: 'average(system.filesystem.free)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_usage.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_usage.ts index bce105387021f..e9a2ff6f93cbe 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_usage.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskUsage: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskUsage', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskUsage', { defaultMessage: 'Disk Usage', }), value: 'average(system.filesystem.used.pct)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_iops.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_iops.ts index 6936f6eee3d6c..881a8bcd05294 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_iops.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_iops.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskIOWrite: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskIOWrite', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskIOWrite', { defaultMessage: 'Disk Write IOPS', }), value: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')", diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_throughput.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_throughput.ts index 6c1a536c7a3a1..2290ed6e3b83f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/disk_write_throughput.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/disk_write_throughput.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const diskWriteThroughput: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.diskWriteThroughput', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskWriteThroughput', { defaultMessage: 'Disk Write Throughput', }), value: "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes: *')", diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/host_count.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/host_count.ts index 1fb01045d6eed..d5cea36319d69 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/host_count.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/host_count.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const hostCount: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.hostCount.hostsLabel', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel', { defaultMessage: 'Hosts', }), value: 'unique_count(host.name)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/index.ts similarity index 94% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/index.ts index 25b21adb7195a..f290d9f894272 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/index.ts @@ -34,7 +34,7 @@ import { memoryCache } from './memory_cache'; import { rx } from './rx'; import { tx } from './tx'; -export const hostLensFormulas = { +export const formulas = { cpuUsage, cpuUsageIowait, cpuUsageIrq, @@ -64,3 +64,6 @@ export const hostLensFormulas = { rx, tx, }; + +export type HostFormulas = typeof formulas; +export type HostFormulaNames = keyof HostFormulas; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_15m.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_15m.ts index 84b865d4081c6..79179241b414d 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_15m.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_15m.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const load15m: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.load15m', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.load15m', { defaultMessage: 'Load (15m)', }), value: 'average(system.load.15)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_1m.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_1m.ts index 8656176be2f04..7346023dc01bb 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_1m.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_1m.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const load1m: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.load1m', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.load1m', { defaultMessage: 'Load (1m)', }), value: 'average(system.load.1)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_5m.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_5m.ts index b76ae333f093d..027d05ef012ba 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/load_5m.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/load_5m.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const load5m: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.load5m', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.load5m', { defaultMessage: 'Load (5m)', }), value: 'average(system.load.5)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/log_rate.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/log_rate.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/log_rate.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/log_rate.ts index 2db54217e3231..21b1309841b7d 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/log_rate.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/log_rate.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const logRate: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.logRate', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.logRate', { defaultMessage: 'Log Rate', }), value: 'differences(cumulative_sum(count()))', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_cache.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_cache.ts index 10e54d99dc62c..3e5b32b256fb7 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_cache.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_cache.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const memoryCache: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.metric.label.cache', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.metric.label.cache', { defaultMessage: 'cache', }), value: 'average(system.memory.used.bytes) - average(system.memory.actual.used.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free.ts similarity index 88% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free.ts index 8b2032a63a5e6..2c07c22650926 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const memoryFree: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.memoryFree', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.memoryFree', { defaultMessage: 'Memory Free', }), value: 'max(system.memory.total) - average(system.memory.actual.used.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free_excluding_cache.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free_excluding_cache.ts index 71f03b974bf32..97df7baed7dfe 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_free_excluding_cache.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_free_excluding_cache.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const memoryFreeExcludingCache: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.metric.label.free', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.metric.label.free', { defaultMessage: 'free', }), value: 'average(system.memory.free)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_usage.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_usage.ts index 59e09889e65a6..8a4641cd365a6 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_usage.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const memoryUsage: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.memoryUsage', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.memoryUsage', { defaultMessage: 'Memory Usage', }), value: 'average(system.memory.actual.used.pct)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_used.ts similarity index 86% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_used.ts index 2c02a22f393be..373164e3705e7 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/memory_used.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/memory_used.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const memoryUsed: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.metric.label.used', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.metric.label.used', { defaultMessage: 'used', }), value: 'average(system.memory.actual.used.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/normalized_load_1m.ts similarity index 87% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/normalized_load_1m.ts index d0e70374829e4..48d130868667f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/normalized_load_1m.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/normalized_load_1m.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const normalizedLoad1m: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.normalizedLoad1m', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.normalizedLoad1m', { defaultMessage: 'Normalized Load', }), value: 'average(system.load.1) / max(system.load.cores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/rx.ts similarity index 90% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/rx.ts index 87536f1a573e2..ec341acf9be6f 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/rx.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/rx.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const rx: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.rx', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.rx', { defaultMessage: 'Network Inbound (RX)', }), value: diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/tx.ts similarity index 90% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/tx.ts index 2aefe2c4cd115..52ada97dd06d6 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/host/tx.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/formulas/tx.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const tx: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.tx', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.tx', { defaultMessage: 'Network Outbound (TX)', }), value: diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/index.ts index e59aaefb2b82b..55ee6e29ee399 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/index.ts @@ -4,79 +4,25 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { snapshot } from './snapshot'; +import { tsvb } from './tsvb'; +import { InventoryMetricsWithDashboards } from '../../types'; +import { type HostFormulas } from './formulas'; +import { type HostDashboards } from './dashboards'; -import { cpu } from './snapshot/cpu'; -import { diskLatency } from './snapshot/disk_latency'; -import { diskSpaceUsage } from './snapshot/disk_space_usage'; -import { count } from '../../shared/metrics/snapshot/count'; -import { load } from './snapshot/load'; -import { logRate } from './snapshot/log_rate'; -import { memory } from './snapshot/memory'; -import { memoryFree } from './snapshot/memory_free'; -import { memoryTotal } from './snapshot/memory_total'; -import { normalizedLoad1m } from './snapshot/normalized_load_1m'; -import { rx } from './snapshot/rx'; -import { tx } from './snapshot/tx'; - -import { hostSystemOverview } from './tsvb/host_system_overview'; -import { hostCpuUsage } from './tsvb/host_cpu_usage'; -import { hostLoad } from './tsvb/host_load'; -import { hostMemoryUsage } from './tsvb/host_memory_usage'; -import { hostNetworkTraffic } from './tsvb/host_network_traffic'; -import { hostFilesystem } from './tsvb/host_filesystem'; - -import { hostK8sOverview } from './tsvb/host_k8s_overview'; -import { hostK8sCpuCap } from './tsvb/host_k8s_cpu_cap'; -import { hostK8sPodCap } from './tsvb/host_k8s_pod_cap'; -import { hostK8sDiskCap } from './tsvb/host_k8s_disk_cap'; -import { hostK8sMemoryCap } from './tsvb/host_k8s_memory_cap'; - -import { hostDockerTop5ByMemory } from './tsvb/host_docker_top_5_by_memory'; -import { hostDockerTop5ByCpu } from './tsvb/host_docker_top_5_by_cpu'; -import { hostDockerOverview } from './tsvb/host_docker_overview'; -import { hostDockerInfo } from './tsvb/host_docker_info'; - -import { InventoryMetrics } from '../../types'; - -const exposedHostSnapshotMetrics = { - cpu, - diskLatency, - diskSpaceUsage, - load, - logRate, - memory, - memoryFree, - memoryTotal, - normalizedLoad1m, - rx, - tx, -}; // not sure why this is the only model with "count" -const hostSnapshotMetrics = { count, ...exposedHostSnapshotMetrics }; +const { count, ...exposedHostSnapshotMetrics } = snapshot; export const hostSnapshotMetricTypes = Object.keys(exposedHostSnapshotMetrics) as Array< keyof typeof exposedHostSnapshotMetrics >; -export const metrics: InventoryMetrics = { - tsvb: { - hostSystemOverview, - hostCpuUsage, - hostLoad, - hostMemoryUsage, - hostNetworkTraffic, - hostFilesystem, - hostK8sOverview, - hostK8sCpuCap, - hostK8sPodCap, - hostK8sDiskCap, - hostK8sMemoryCap, - hostDockerOverview, - hostDockerInfo, - hostDockerTop5ByMemory, - hostDockerTop5ByCpu, - }, - snapshot: hostSnapshotMetrics, +export const metrics: InventoryMetricsWithDashboards = { + tsvb, + snapshot, + getFormulas: async () => await import('./formulas').then(({ formulas }) => ({ ...formulas })), + getDashboards: async () => + await import('./dashboards').then(({ dashboards }) => ({ ...dashboards })), defaultSnapshot: 'cpu', defaultTimeRangeInSeconds: 3600, // 1 hour }; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts new file mode 100644 index 0000000000000..8cf567b40165e --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.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 { cpu } from './cpu'; +import { diskLatency } from './disk_latency'; +import { diskSpaceUsage } from './disk_space_usage'; +import { count } from '../../../shared/metrics/snapshot/count'; +import { load } from './load'; +import { logRate } from './log_rate'; +import { memory } from './memory'; +import { memoryFree } from './memory_free'; +import { memoryTotal } from './memory_total'; +import { normalizedLoad1m } from './normalized_load_1m'; +import { rx } from './rx'; +import { tx } from './tx'; + +export const snapshot = { + cpu, + diskLatency, + diskSpaceUsage, + count, + load, + logRate, + memory, + memoryFree, + memoryTotal, + normalizedLoad1m, + rx, + tx, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.ts new file mode 100644 index 0000000000000..fb91ee9dd8b27 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/tsvb/index.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 { hostSystemOverview } from './host_system_overview'; +import { hostCpuUsage } from './host_cpu_usage'; +import { hostLoad } from './host_load'; +import { hostMemoryUsage } from './host_memory_usage'; +import { hostNetworkTraffic } from './host_network_traffic'; +import { hostFilesystem } from './host_filesystem'; + +import { hostK8sOverview } from './host_k8s_overview'; +import { hostK8sCpuCap } from './host_k8s_cpu_cap'; +import { hostK8sPodCap } from './host_k8s_pod_cap'; +import { hostK8sDiskCap } from './host_k8s_disk_cap'; +import { hostK8sMemoryCap } from './host_k8s_memory_cap'; + +import { hostDockerTop5ByMemory } from './host_docker_top_5_by_memory'; +import { hostDockerTop5ByCpu } from './host_docker_top_5_by_cpu'; +import { hostDockerOverview } from './host_docker_overview'; +import { hostDockerInfo } from './host_docker_info'; + +export const tsvb = { + hostSystemOverview, + hostCpuUsage, + hostLoad, + hostMemoryUsage, + hostNetworkTraffic, + hostFilesystem, + hostK8sOverview, + hostK8sCpuCap, + hostK8sPodCap, + hostK8sDiskCap, + hostK8sMemoryCap, + hostDockerOverview, + hostDockerInfo, + hostDockerTop5ByMemory, + hostDockerTop5ByCpu, +}; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/index.ts index 6d683f287c991..f3199f21593d1 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/index.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { POD_FIELD, HOST_FIELD, CONTAINER_FIELD } from '../constants'; import { host } from './host'; -import { pod } from './pod'; +import { pod } from './kubernetes/pod'; import { awsEC2 } from './aws_ec2'; import { awsS3 } from './aws_s3'; import { awsRDS } from './aws_rds'; @@ -17,9 +17,21 @@ import { container } from './container'; import { InventoryItemType } from './types'; export { metrics } from './metrics'; -export const inventoryModels = [host, pod, container, awsEC2, awsS3, awsRDS, awsSQS]; +const catalog = { + host, + pod, + container, + awsEC2, + awsS3, + awsRDS, + awsSQS, +} as const; -export const findInventoryModel = (type: InventoryItemType) => { +export const inventoryModels = Object.values(catalog); + +type InventoryModels = typeof catalog[T]; + +export const findInventoryModel = (type: T): InventoryModels => { const model = inventoryModels.find((m) => m.id === type); if (!model) { throw new Error( @@ -28,7 +40,8 @@ export const findInventoryModel = (type: InventoryItemType) => { }) ); } - return model; + + return model as InventoryModels; }; const LEGACY_TYPES = ['host', 'pod', 'container']; diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/index.ts similarity index 95% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/index.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/index.ts index f7593588f254d..5203d1a0abf31 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/index.ts @@ -14,7 +14,7 @@ import { nodeMemoryUsed } from './node_memory_used'; import { nodePodCapacity } from './node_pod_capacity'; import { nodePodUsed } from './node_pod_used'; -export const kubernetesLensFormulas = { +export const formulas = { nodeCpuCapacity, nodeCpuUsed, nodeDiskCapacity, diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_capacity.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_capacity.ts similarity index 77% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_capacity.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_capacity.ts index 1be71eb6c1a56..952edac1d5de4 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_capacity.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_capacity.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeCpuCapacity: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.capacity', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.capacity', { defaultMessage: 'Capacity', }), value: 'max(kubernetes.node.cpu.allocatable.cores) * 1000000000', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_used.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_used.ts similarity index 77% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_used.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_used.ts index ece5d4437961f..4561303430d15 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_cpu_used.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_cpu_used.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeCpuUsed: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.used', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.used', { defaultMessage: 'Used', }), value: 'average(kubernetes.node.cpu.usage.nanocores)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_capacity.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_capacity.ts similarity index 76% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_capacity.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_capacity.ts index ef30107970d04..45e0e61c1b8f6 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_capacity.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_capacity.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeDiskCapacity: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.capacity', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.capacity', { defaultMessage: 'Capacity', }), value: 'max(kubernetes.node.fs.capacity.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_used.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_used.ts similarity index 76% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_used.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_used.ts index 8a38f22cfe141..4f6d07a0a4379 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_disk_used.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_disk_used.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeDiskUsed: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.used', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.used', { defaultMessage: 'Used', }), value: 'average(kubernetes.node.fs.used.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_capacity.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_capacity.ts similarity index 76% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_capacity.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_capacity.ts index 379580c7fba30..4607f002194a9 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_capacity.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_capacity.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeMemoryCapacity: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.capacity', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.capacity', { defaultMessage: 'Capacity', }), value: 'max(kubernetes.node.memory.allocatable.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_used.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_used.ts similarity index 76% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_used.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_used.ts index 34d5bffebd92a..cdcaf32593a3c 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_memory_used.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_memory_used.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodeMemoryUsed: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.used', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.used', { defaultMessage: 'Used', }), value: 'average(kubernetes.node.memory.usage.bytes)', diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_capacity.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_capacity.ts similarity index 78% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_capacity.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_capacity.ts index d3990328c0552..9bf455df9ccf8 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_capacity.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_capacity.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodePodCapacity: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.capacity', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.capacity', { defaultMessage: 'Capacity', }), value: diff --git a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_used.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_used.ts similarity index 76% rename from x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_used.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_used.ts index 6c3e3e952d635..697ceaca02f67 100644 --- a/x-pack/plugins/infra/public/common/visualizations/lens/formulas/kubernetes/node_pod_used.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/formulas/node_pod_used.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; +import type { FormulaValueConfig } from '@kbn/lens-embeddable-utils'; export const nodePodUsed: FormulaValueConfig = { - label: i18n.translate('xpack.infra.assetDetails.formulas.kubernetes.used', { + label: i18n.translate('xpack.metricsData.assetDetails.formulas.kubernetes.used', { defaultMessage: 'Used', }), value: 'unique_count(kubernetes.pod.uid)', diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/index.ts new file mode 100644 index 0000000000000..7340df0d8c049 --- /dev/null +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/node/metrics/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { formulas } from './formulas'; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/index.ts similarity index 85% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/index.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/index.ts index 3a8cf778c00f3..a61abbfeb01e3 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/index.ts @@ -7,12 +7,12 @@ import { i18n } from '@kbn/i18n'; import { metrics } from './metrics'; -import { InventoryModel } from '../types'; -import { nginx as nginxRequiredMetrics } from '../shared/metrics/required_metrics'; +import { InventoryModel } from '../../types'; +import { nginx as nginxRequiredMetrics } from '../../shared/metrics/required_metrics'; export { podSnapshotMetricTypes } from './metrics'; -export const pod: InventoryModel = { +export const pod: InventoryModel = { id: 'pod', displayName: i18n.translate('xpack.metricsData.inventoryModel.pod.displayName', { defaultMessage: 'Kubernetes Pods', diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/index.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/index.ts similarity index 95% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/index.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/index.ts index 25ec090da3a5d..eeae2c2ec586b 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/index.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/index.ts @@ -15,7 +15,7 @@ import { podCpuUsage } from './tsvb/pod_cpu_usage'; import { podLogUsage } from './tsvb/pod_log_usage'; import { podMemoryUsage } from './tsvb/pod_memory_usage'; import { podNetworkTraffic } from './tsvb/pod_network_traffic'; -import { InventoryMetrics } from '../../types'; +import { InventoryMetrics } from '../../../types'; const podSnapshotMetrics = { cpu, memory, rx, tx }; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/cpu.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/cpu.ts similarity index 93% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/cpu.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/cpu.ts index fe8c0d1740e66..5a190a1530b5b 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/cpu.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/cpu.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MetricsUIAggregation } from '../../../types'; +import { MetricsUIAggregation } from '../../../../types'; export const cpu: MetricsUIAggregation = { cpu_with_limit: { diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/memory.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/memory.ts similarity index 93% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/memory.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/memory.ts index 480fdc055a03f..70640bdc2ac6c 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/memory.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/memory.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MetricsUIAggregation } from '../../../types'; +import { MetricsUIAggregation } from '../../../../types'; export const memory: MetricsUIAggregation = { memory_with_limit: { diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/rx.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/rx.ts similarity index 79% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/rx.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/rx.ts index c2162499c32d7..8108b699eb299 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/rx.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/rx.ts @@ -5,5 +5,5 @@ * 2.0. */ -import { networkTraffic } from '../../../shared/metrics/snapshot/network_traffic'; +import { networkTraffic } from '../../../../shared/metrics/snapshot/network_traffic'; export const rx = networkTraffic('rx', 'kubernetes.pod.network.rx.bytes'); diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/tx.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/tx.ts similarity index 79% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/tx.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/tx.ts index 4d43262e904fb..4a0e1d6e4e165 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/snapshot/tx.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/snapshot/tx.ts @@ -5,5 +5,5 @@ * 2.0. */ -import { networkTraffic } from '../../../shared/metrics/snapshot/network_traffic'; +import { networkTraffic } from '../../../../shared/metrics/snapshot/network_traffic'; export const tx = networkTraffic('tx', 'kubernetes.pod.network.tx.bytes'); diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_cpu_usage.ts similarity index 99% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_cpu_usage.ts index a7574fc68813f..c1d4234725ae6 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_cpu_usage.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; +import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../../types'; export const podCpuUsage: TSVBMetricModelCreator = ( timeField, diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_log_usage.ts similarity index 99% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_log_usage.ts index 0e2a588463160..e57b839c4d0c2 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_log_usage.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; +import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../../types'; export const podLogUsage: TSVBMetricModelCreator = ( timeField, diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_memory_usage.ts similarity index 99% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_memory_usage.ts index 9c774e1b18ed0..408d7d21386b5 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_memory_usage.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; +import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../../types'; export const podMemoryUsage: TSVBMetricModelCreator = ( timeField, diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_network_traffic.ts similarity index 99% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_network_traffic.ts index c7a2db09497ab..b95fd8e2f463b 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_network_traffic.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; +import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../../types'; export const podNetworkTraffic: TSVBMetricModelCreator = ( timeField, diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_overview.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_overview.ts similarity index 99% rename from x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_overview.ts rename to x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_overview.ts index 0fe94c7f53dab..f9789dff288c5 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/pod/metrics/tsvb/pod_overview.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/kubernetes/pod/metrics/tsvb/pod_overview.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../types'; +import { TSVBMetricModelCreator, TSVBMetricModel } from '../../../../types'; export const podOverview: TSVBMetricModelCreator = ( timeField, diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/metrics.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/metrics.ts index 0f73cdb6ef3b1..00937a064a7b8 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/metrics.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/metrics.ts @@ -7,7 +7,7 @@ import { metrics as hostMetrics } from './host/metrics'; import { metrics as sharedMetrics } from './shared/metrics'; -import { metrics as podMetrics } from './pod/metrics'; +import { metrics as podMetrics } from './kubernetes/pod/metrics'; import { metrics as containerMetrics } from './container/metrics'; import { metrics as awsEC2Metrics } from './aws_ec2/metrics'; import { metrics as awsS3Metrics } from './aws_s3/metrics'; diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/types.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/types.ts index 5bc36429e2ba2..25bc3ab001b2d 100644 --- a/x-pack/plugins/metrics_data_access/common/inventory_models/types.ts +++ b/x-pack/plugins/metrics_data_access/common/inventory_models/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { ChartModel, FormulaValueConfig } from '@kbn/lens-embeddable-utils'; import * as rt from 'io-ts'; export const ItemTypeRT = rt.keyof({ @@ -384,11 +385,21 @@ export interface InventoryMetrics { defaultTimeRangeInSeconds: number; } -export interface InventoryModel { +export interface InventoryMetricsWithDashboards< + TFormula extends Record, + TDashboard extends Record +> extends InventoryMetrics { + getFormulas: () => Promise; + getDashboards: () => Promise; +} + +type Modules = 'aws' | 'docker' | 'system' | 'kubernetes'; + +export interface InventoryModel { id: string; displayName: string; singularDisplayName: string; - requiredModule: string; + requiredModule: Modules; fields: { id: string; name: string; @@ -402,8 +413,17 @@ export interface InventoryModel { apm: boolean; uptime: boolean; }; - metrics: InventoryMetrics; + metrics: TMetrics; requiredMetrics: InventoryMetric[]; tooltipMetrics: SnapshotMetricType[]; nodeFilter?: object[]; } + +export interface DashboardFn { + get: (...args: any[]) => DashboardModel; +} + +export interface DashboardModel { + charts: ChartModel[]; + dependsOn?: string[]; +} diff --git a/x-pack/plugins/metrics_data_access/tsconfig.json b/x-pack/plugins/metrics_data_access/tsconfig.json index 5eecf2fc31735..5f387443ed53a 100644 --- a/x-pack/plugins/metrics_data_access/tsconfig.json +++ b/x-pack/plugins/metrics_data_access/tsconfig.json @@ -33,6 +33,7 @@ "@kbn/observability-shared-plugin", "@kbn/i18n-react", "@kbn/logging", - "@kbn/core-http-request-handler-context-server" + "@kbn/core-http-request-handler-context-server", + "@kbn/lens-embeddable-utils" ] } diff --git a/x-pack/plugins/ml/common/openapi/ml_apis.yaml b/x-pack/plugins/ml/common/openapi/ml_apis.yaml index 5b7a68309c940..9f2a7de498184 100644 --- a/x-pack/plugins/ml/common/openapi/ml_apis.yaml +++ b/x-pack/plugins/ml/common/openapi/ml_apis.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Machine learning APIs description: Kibana APIs for the machine learning feature - version: "1.0.1" + version: "1.0.2" license: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license @@ -82,7 +82,8 @@ components: required: false schema: type: boolean - example: 'true' + examples: + - true securitySchemes: basicAuth: type: http @@ -187,12 +188,14 @@ components: properties: error: type: string - example: Unauthorized + examples: + - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 examples: mlSyncExample: summary: Two anomaly detection jobs required synchronization in this example. diff --git a/x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml b/x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml index 649bc3db06acc..6ff44e29517e6 100644 --- a/x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml +++ b/x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml @@ -1,8 +1,8 @@ -openapi: 3.0.1 +openapi: 3.1.0 info: title: Machine learning APIs description: Kibana APIs for the machine learning feature - version: "1.0.1" + version: "1.0.2" license: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license @@ -46,7 +46,8 @@ components: required: false schema: type: boolean - example: 'true' + examples: + - true securitySchemes: apiKeyAuth: type: apiKey @@ -148,12 +149,14 @@ components: properties: error: type: string - example: Unauthorized + examples: + - Unauthorized message: type: string statusCode: type: integer - example: 401 + examples: + - 401 examples: mlSyncExample: summary: Two anomaly detection jobs required synchronization in this example. diff --git a/x-pack/plugins/ml/common/types/ml_server_info.ts b/x-pack/plugins/ml/common/types/ml_server_info.ts index e5141d6f2e78f..215f46c26bef8 100644 --- a/x-pack/plugins/ml/common/types/ml_server_info.ts +++ b/x-pack/plugins/ml/common/types/ml_server_info.ts @@ -20,6 +20,8 @@ export interface MlServerDefaults { export interface MlServerLimits { max_model_memory_limit?: string; effective_max_model_memory_limit?: string; + max_single_ml_node_processors?: number; + total_ml_processors?: number; } export interface MlInfoResponse { diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index e617d05f94d31..aecbb0aa01089 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -101,7 +101,7 @@ export interface ScatterplotMatrixProps { legendType?: LegendType; searchQuery?: estypes.QueryDslQueryContainer; runtimeMappings?: RuntimeMappings; - indexPattern?: DataView; + dataView?: DataView; query?: Query; } @@ -113,7 +113,7 @@ export const ScatterplotMatrix: FC = ({ legendType, searchQuery, runtimeMappings, - indexPattern, + dataView, query, }) => { const { esSearch } = useMlApiContext(); @@ -210,9 +210,7 @@ export const ScatterplotMatrix: FC = ({ vegaSpec.data = { url: { '%context%': true, - ...(indexPattern?.timeFieldName - ? { ['%timefield%']: `${indexPattern?.timeFieldName}` } - : {}), + ...(dataView?.timeFieldName ? { ['%timefield%']: `${dataView?.timeFieldName}` } : {}), index, body: { fields: fieldsToFetch, @@ -300,7 +298,7 @@ export const ScatterplotMatrix: FC = ({ } const combinedRuntimeMappings = - indexPattern && getCombinedRuntimeMappings(indexPattern, runtimeMappings); + dataView && getCombinedRuntimeMappings(dataView, runtimeMappings); const body = { fields: queryFields, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts index 6cd5c40d66e4a..139b49f7c2b45 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts @@ -37,12 +37,10 @@ export const useResultsViewConfig = (jobId: string) => { } = useMlKibana(); const trainedModelsApiService = useTrainedModelsApiService(); - const [indexPattern, setIndexPattern] = useState(undefined); - const [indexPatternErrorMessage, setIndexPatternErrorMessage] = useState( - undefined - ); + const [dataView, setDataView] = useState(undefined); + const [dataViewErrorMessage, setDataViewErrorMessage] = useState(undefined); const [isInitialized, setIsInitialized] = useState(false); - const [needsDestIndexPattern, setNeedsDestIndexPattern] = useState(false); + const [needsDestDataView, setNeedsDestDataView] = useState(false); const [isLoadingJobConfig, setIsLoadingJobConfig] = useState(false); const [jobConfig, setJobConfig] = useState(undefined); const [jobCapsServiceErrorMessage, setJobCapsServiceErrorMessage] = useState( @@ -100,39 +98,39 @@ export const useResultsViewConfig = (jobId: string) => { try { const destIndex = getDestinationIndex(jobConfigUpdate); const destDataViewId = (await getDataViewIdFromName(destIndex)) ?? destIndex; - let dataView: DataView | undefined; + let fetchedDataView: DataView | undefined; try { - dataView = await dataViews.get(destDataViewId); + fetchedDataView = await dataViews.get(destDataViewId); // Force refreshing the fields list here because a user directly coming // from the job creation wizard might land on the page without the // data view being fully initialized because it was created // before the analytics job populated the destination index. - await dataViews.refreshFields(dataView); + await dataViews.refreshFields(fetchedDataView); } catch (e) { - dataView = undefined; + fetchedDataView = undefined; } - if (dataView === undefined) { - setNeedsDestIndexPattern(true); + if (fetchedDataView === undefined) { + setNeedsDestDataView(true); const sourceIndex = jobConfigUpdate.source.index[0]; const sourceDataViewId = (await getDataViewIdFromName(sourceIndex)) ?? sourceIndex; try { - dataView = await dataViews.get(sourceDataViewId); + fetchedDataView = await dataViews.get(sourceDataViewId); } catch (e) { - dataView = undefined; + fetchedDataView = undefined; } } - if (dataView !== undefined) { - await newJobCapsServiceAnalytics.initializeFromDataVIew(dataView); + if (fetchedDataView !== undefined) { + await newJobCapsServiceAnalytics.initializeFromDataVIew(fetchedDataView); setJobConfig(analyticsConfigs.data_frame_analytics[0]); - setIndexPattern(dataView); + setDataView(fetchedDataView); setIsInitialized(true); setIsLoadingJobConfig(false); } else { - setIndexPatternErrorMessage( + setDataViewErrorMessage( i18n.translate('xpack.ml.dataframe.analytics.results.dataViewMissingErrorMessage', { defaultMessage: 'To view this page, a Kibana data view is necessary for either the destination or source index of this analytics job.', @@ -153,15 +151,15 @@ export const useResultsViewConfig = (jobId: string) => { }, []); return { - indexPattern, - indexPatternErrorMessage, + dataView, + dataViewErrorMessage, isInitialized, isLoadingJobConfig, jobCapsServiceErrorMessage, jobConfig, jobConfigErrorMessage, jobStatus, - needsDestIndexPattern, + needsDestDataView, totalFeatureImportance, }; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index c574a6f661085..d065ef84fa970 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -356,9 +356,9 @@ export const ConfigurationStepForm: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const indexPatternFieldsTableItems = useMemo(() => { - if (indexData?.indexPatternFields !== undefined) { - return indexData.indexPatternFields.map((field) => ({ + const dataViewFieldsTableItems = useMemo(() => { + if (indexData?.dataViewFields !== undefined) { + return indexData.dataViewFields.map((field) => ({ name: field, is_included: false, is_required: false, @@ -366,7 +366,7 @@ export const ConfigurationStepForm: FC = ({ } return []; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [`${indexData?.indexPatternFields}`]); + }, [`${indexData?.dataViewFields}`]); useEffect(() => { if (typeof savedSearchQueryStr === 'string') { @@ -377,11 +377,11 @@ export const ConfigurationStepForm: FC = ({ useEffect(() => { if (isJobTypeWithDepVar) { - const indexPatternRuntimeFields = getCombinedRuntimeMappings(selectedDataView); + const dataViewRuntimeFields = getCombinedRuntimeMappings(selectedDataView); let runtimeOptions; - if (indexPatternRuntimeFields) { - runtimeOptions = getRuntimeDepVarOptions(jobType, indexPatternRuntimeFields); + if (dataViewRuntimeFields) { + runtimeOptions = getRuntimeDepVarOptions(jobType, dataViewRuntimeFields); } loadDepVarOptions(form, runtimeOptions); @@ -527,7 +527,7 @@ export const ConfigurationStepForm: FC = ({ legendType: getScatterplotMatrixLegendType(jobType), searchQuery: jobConfigQuery, runtimeMappings, - indexPattern: selectedDataView, + dataView: selectedDataView, }), // eslint-disable-next-line react-hooks/exhaustive-deps [ @@ -571,7 +571,7 @@ export const ConfigurationStepForm: FC = ({ const tableItems = includesTableItems.length > 0 && !noDocsContainMappedFields ? includesTableItems - : indexPatternFieldsTableItems; + : dataViewFieldsTableItems; return ( = ({ fullWidth > diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx index dab0359457786..fb29df680eda6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx @@ -195,7 +195,7 @@ export const CreateAnalyticsAdvancedEditor: FC = (prop ))} - + ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx index c32530658e6f8..63b31ec37feb9 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step/create_step.tsx @@ -5,62 +5,46 @@ * 2.0. */ -import React, { FC, useEffect, useMemo, useState } from 'react'; +import React, { FC, useState } from 'react'; import { EuiButton, - EuiCheckbox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer, - EuiText, + EuiSwitch, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { CreateDataViewForm } from '@kbn/ml-data-view-utils/components/create_data_view_form_row'; -import { useMlKibana } from '../../../../../contexts/kibana'; import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { Messages } from '../shared'; import { ANALYTICS_STEPS } from '../../page'; +import { useCanCreateDataView } from '../../hooks/use_can_create_data_view'; +import { useDataViewTimeFields } from '../../hooks/use_data_view_time_fields'; import { CreateStepFooter } from '../create_step_footer'; interface Props extends CreateAnalyticsFormProps { step: ANALYTICS_STEPS; + showCreateDataView?: boolean; } -export const CreateStep: FC = ({ actions, state, step }) => { - const { - services: { - application: { capabilities }, - }, - } = useMlKibana(); - - const canCreateDataView = useMemo( - () => - capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true, - [capabilities] - ); +export const CreateStep: FC = ({ actions, state, step, showCreateDataView = false }) => { + const canCreateDataView = useCanCreateDataView(); + const { dataViewAvailableTimeFields, onTimeFieldChanged } = useDataViewTimeFields({ + actions, + state, + }); const { createAnalyticsJob, setFormState, startAnalyticsJob } = actions; const { isAdvancedEditorValidJson, isJobCreated, isJobStarted, isValid, requestMessages } = state; - const { - createIndexPattern, - destinationIndex, - destinationIndexPatternTitleExists, - jobId, - jobType, - } = state.form; + const { createDataView, destinationDataViewTitleExists, jobId, jobType, timeFieldName } = + state.form; const [startChecked, setStartChecked] = useState(true); const [creationTriggered, setCreationTriggered] = useState(false); const [showProgress, setShowProgress] = useState(false); - useEffect(() => { - if (canCreateDataView === false) { - setFormState({ createIndexPattern: false }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [capabilities]); - if (step !== ANALYTICS_STEPS.CREATE) return null; const handleCreation = async () => { @@ -80,125 +64,62 @@ export const CreateStep: FC = ({ actions, state, step }) => { return (
    {!isJobCreated && !isJobStarted && ( - - - - - - { - setStartChecked(e.target.checked); - if (e.target.checked === false) { - setFormState({ createIndexPattern: false }); - } - }} - /> - - - {startChecked ? ( - - - {i18n.translate( - 'xpack.ml.dataframe.analytics.create.dataViewPermissionWarning', - { - defaultMessage: 'You need permission to create data views.', - } - )} - , - ] - : []), - ...(createIndexPattern && destinationIndexPatternTitleExists - ? [ - i18n.translate( - 'xpack.ml.dataframe.analytics.create.dataViewExistsError', - { - defaultMessage: - 'A data view with the title {title} already exists.', - values: { title: destinationIndex }, - } - ), - ] - : []), - ...(!createIndexPattern && !destinationIndexPatternTitleExists - ? [ - - {i18n.translate( - 'xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage', - { - defaultMessage: - 'You may not be able to view job results if a data view is not created for the destination index.', - } - )} - , - ] - : []), - ]} - > - setFormState({ createIndexPattern: !createIndexPattern })} - data-test-subj="mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox" - /> - - - ) : null} - - - - - {i18n.translate('xpack.ml.dataframe.analytics.create.wizardCreateButton', { - defaultMessage: 'Create', - })} - - - + <> + {showCreateDataView && ( + <> + setFormState({ createDataView: !createDataView })} + dataViewAvailableTimeFields={dataViewAvailableTimeFields} + dataViewTimeField={timeFieldName} + onTimeFieldChanged={onTimeFieldChanged} + /> + + + )} + + + + + { + setStartChecked(e.target.checked); + }} + /> + + + + + {i18n.translate('xpack.ml.dataframe.analytics.create.wizardCreateButton', { + defaultMessage: 'Create', + })} + + + + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx index 2a86ea3f723c6..1948915da69cf 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx @@ -5,11 +5,13 @@ * 2.0. */ -import React, { FC, Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { FC, Fragment, useEffect, useMemo, useRef, useState } from 'react'; import { debounce } from 'lodash'; -import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiSwitch, EuiTextArea } from '@elastic/eui'; +import { EuiFieldText, EuiFormRow, EuiSpacer, EuiSwitch, EuiTextArea } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import { CreateDataViewForm } from '@kbn/ml-data-view-utils/components/create_data_view_form_row'; +import { DestinationIndexForm } from '@kbn/ml-creation-wizard-utils/components/destination_index_form'; import { useMlKibana } from '../../../../../contexts/kibana'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; @@ -17,8 +19,8 @@ import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validat import { ContinueButton } from '../continue_button'; import { ANALYTICS_STEPS } from '../../page'; import { ml } from '../../../../../services/ml_api_service'; -import { useDataSource } from '../../../../../contexts/ml'; -import { DetailsStepTimeField } from './details_step_time_field'; +import { useCanCreateDataView } from '../../hooks/use_can_create_data_view'; +import { useDataViewTimeFields } from '../../hooks/use_data_view_time_fields'; import { AdditionalSection } from './additional_section'; const DEFAULT_RESULTS_FIELD = 'ml'; @@ -40,13 +42,19 @@ export const DetailsStepForm: FC = ({ services: { docLinks, notifications }, } = useMlKibana(); - const { selectedDataView } = useDataSource(); + const canCreateDataView = useCanCreateDataView(); + const { dataViewAvailableTimeFields, onTimeFieldChanged } = useDataViewTimeFields({ + actions, + state, + }); const createIndexLink = docLinks.links.apis.createIndex; const { setFormState } = actions; const { form, cloneJob, hasSwitchedToEditor, isJobCreated } = state; const { + createDataView, description, + destinationDataViewTitleExists, destinationIndex, destinationIndexNameEmpty, destinationIndexNameExists, @@ -67,41 +75,6 @@ export const DetailsStepForm: FC = ({ (cloneJob === undefined && hasSwitchedToEditor === false && resultsField === undefined) || (cloneJob !== undefined && resultsField === DEFAULT_RESULTS_FIELD) ); - const [dataViewAvailableTimeFields, setDataViewAvailableTimeFields] = useState([]); - - const onTimeFieldChanged = useCallback( - (e: React.ChangeEvent) => { - const value = e.target.value; - // If the value is an empty string, it's not a valid selection - if (value === '') { - return; - } - // Find the time field based on the selected value - // this is to account for undefined when user chooses not to use a date field - const timeField = dataViewAvailableTimeFields.find((col) => col === value); - - setFormState({ timeFieldName: timeField }); - }, - [dataViewAvailableTimeFields, setFormState] - ); - - useEffect(() => { - // Default timeFieldName to the source data view's time field if it exists - if (selectedDataView !== undefined) { - setFormState({ timeFieldName: selectedDataView.timeFieldName }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - // Get possible timefields for the results data view - if (selectedDataView !== undefined) { - const timefields = selectedDataView.fields - .filter((f) => f.type === 'date') - .map((f) => f.name); - setDataViewAvailableTimeFields(timefields); - } - }, [selectedDataView, setFormState]); const forceInput = useRef(null); @@ -110,7 +83,8 @@ export const DetailsStepForm: FC = ({ jobIdExists === true || jobIdValid === false || destinationIndexNameEmpty === true || - destinationIndexNameValid === false; + destinationIndexNameValid === false || + (createDataView && destinationDataViewTitleExists === true); const debouncedIndexCheck = debounce(async () => { try { @@ -265,73 +239,24 @@ export const DetailsStepForm: FC = ({ data-test-subj="mlDFAnalyticsJobCreationJobDescription" /> - - setDestIndexSameAsId(!destIndexSameAsId)} - data-test-subj="mlAnalyticsCreateJobWizardDestIndexSameAsIdSwitch" - /> - - {destIndexSameAsId === false && ( - - {i18n.translate( - 'xpack.ml.dataframe.analytics.create.destinationIndexInvalidError', - { - defaultMessage: 'Invalid destination index name.', - } - )} -
    - - {i18n.translate( - 'xpack.ml.dataframe.stepDetailsForm.destinationIndexInvalidErrorLink', - { - defaultMessage: 'Learn more about index name limitations.', - } - )} - - , - ] + setFormState({ destinationIndex: d })} + setDestIndexSameAsId={setDestIndexSameAsId} + switchLabel={i18n.translate( + 'xpack.ml.dataframe.analytics.create.destinationIndexFormSwitchLabel', + { + defaultMessage: 'Use job ID as destination index name', } - > - setFormState({ destinationIndex: e.target.value })} - aria-label={i18n.translate( - 'xpack.ml.dataframe.analytics.create.destinationIndexInputAriaLabel', - { - defaultMessage: 'Choose a unique destination index name.', - } - )} - isInvalid={!destinationIndexNameEmpty && !destinationIndexNameValid} - data-test-subj="mlAnalyticsCreateJobFlyoutDestinationIndexInput" - /> -
    - )} + )} + /> = ({ /> )} - {destinationIndexNameValid && dataViewAvailableTimeFields.length > 0 ? ( - - ) : null} + setFormState({ createDataView: !createDataView })} + dataViewAvailableTimeFields={dataViewAvailableTimeFields} + dataViewTimeField={timeFieldName} + onTimeFieldChanged={onTimeFieldChanged} + /> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_time_field.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_time_field.tsx deleted file mode 100644 index d7d88325fd1da..0000000000000 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_time_field.tsx +++ /dev/null @@ -1,69 +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 React, { FC } from 'react'; -import { EuiFormRow, EuiSelect } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; - -interface Props { - dataViewAvailableTimeFields: string[]; - dataViewTimeField: string | undefined; - onTimeFieldChanged: (e: React.ChangeEvent) => void; -} - -export const DetailsStepTimeField: FC = ({ - dataViewAvailableTimeFields, - dataViewTimeField, - onTimeFieldChanged, -}) => { - const noTimeFieldLabel = i18n.translate( - 'xpack.ml.dataframe.analytics.create.detailsStep.noTimeFieldOptionLabel', - { - defaultMessage: "I don't want to use the time field option", - } - ); - - const noTimeFieldOption = { - text: noTimeFieldLabel, - value: undefined, - }; - - const disabledDividerOption = { - disabled: true, - text: '───', - value: '', - }; - - return ( - - } - helpText={ - - } - > - ({ text })), - disabledDividerOption, - noTimeFieldOption, - ]} - value={dataViewTimeField} - onChange={onTimeFieldChanged} - data-test-subj="mlDataFrameAnalyticsCreateDataViewTimeFieldSelect" - /> - - ); -}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_can_create_data_view.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_can_create_data_view.ts new file mode 100644 index 0000000000000..44a362c82a60a --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_can_create_data_view.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; + +import { useMlKibana } from '../../../../contexts/kibana'; + +export const useCanCreateDataView = () => { + const { + services: { + application: { capabilities }, + }, + } = useMlKibana(); + + return useMemo( + () => + capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true, + [capabilities] + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_data_view_time_fields.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_data_view_time_fields.ts new file mode 100644 index 0000000000000..624545f0b88ce --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_data_view_time_fields.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; + +import { useDataSource } from '../../../../contexts/ml'; + +import { CreateAnalyticsFormProps } from '../../analytics_management/hooks/use_create_analytics_form'; + +export const useDataViewTimeFields = ({ actions, state }: CreateAnalyticsFormProps) => { + const { setFormState } = actions; + + const { selectedDataView } = useDataSource(); + + const [dataViewAvailableTimeFields, setDataViewAvailableTimeFields] = useState([]); + + const onTimeFieldChanged = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + // If the value is an empty string, it's not a valid selection + if (value === '') { + return; + } + // Find the time field based on the selected value + // this is to account for undefined when user chooses not to use a date field + const timeField = dataViewAvailableTimeFields.find((col) => col === value); + + setFormState({ timeFieldName: timeField }); + }, + [dataViewAvailableTimeFields, setFormState] + ); + + useEffect(() => { + // Default timeFieldName to the source data view's time field if it exists + if (selectedDataView !== undefined) { + setFormState({ timeFieldName: selectedDataView.timeFieldName }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + // Get possible timefields for the results data view + if (selectedDataView !== undefined) { + const timefields = selectedDataView.fields + .filter((f) => f.type === 'date') + .map((f) => f.name); + setDataViewAvailableTimeFields(timefields); + } + }, [selectedDataView, setFormState]); + + return { dataViewAvailableTimeFields, onTimeFieldChanged }; +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index cf3c0fcf05ef2..2336113f917da 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -25,7 +25,7 @@ import { getFieldType, getDataGridSchemaFromKibanaFieldType, getDataGridSchemaFromESFieldType, - getFieldsFromKibanaIndexPattern, + getFieldsFromKibanaDataView, showDataGridColumnChartErrorMessageToast, useDataGrid, useRenderCellValue, @@ -58,8 +58,8 @@ function getRuntimeFieldColumns(runtimeMappings: RuntimeMappings) { }); } -function getIndexPatternColumns(indexPattern: DataView, fieldsFilter: string[]) { - const { fields } = indexPattern; +function getDataViewColumns(dataView: DataView, fieldsFilter: string[]) { + const { fields } = dataView; return fields .filter((field) => fieldsFilter.includes(field.name)) @@ -78,7 +78,7 @@ function getIndexPatternColumns(indexPattern: DataView, fieldsFilter: string[]) } export const useIndexData = ( - indexPattern: DataView, + dataView: DataView, query: Record | undefined, toastNotifications: CoreSetup['notifications']['toasts'], runtimeMappings?: RuntimeMappings @@ -87,7 +87,7 @@ export const useIndexData = ( // This is a workaround to avoid passing potentially thousands of unpopulated fields // (for example, as part of filebeat/metricbeat/ECS based indices) // to the data grid component which would significantly slow down the page. - const [indexPatternFields, setIndexPatternFields] = useState(); + const [dataViewFields, setDataViewFields] = useState(); const [timeRangeMs, setTimeRangeMs] = useState(); useEffect(() => { @@ -96,7 +96,7 @@ export const useIndexData = ( setStatus(INDEX_STATUS.LOADING); const esSearchRequest = { - index: indexPattern.title, + index: dataView.title, body: { fields: ['*'], _source: false, @@ -116,13 +116,13 @@ export const useIndexData = ( // Get all field names for each returned doc and flatten it // to a list of unique field names used across all docs. - const allDataViewFields = getFieldsFromKibanaIndexPattern(indexPattern); + const allDataViewFields = getFieldsFromKibanaDataView(dataView); const populatedFields = [...new Set(docs.map(Object.keys).flat(1))] .filter((d) => allDataViewFields.includes(d)) .sort(); setStatus(INDEX_STATUS.LOADED); - setIndexPatternFields(populatedFields); + setDataViewFields(populatedFields); } catch (e) { setErrorMessage(extractErrorMessage(e)); setStatus(INDEX_STATUS.ERROR); @@ -136,20 +136,20 @@ export const useIndexData = ( // To be used for data grid column selection // and will be applied to doc and chart queries. const combinedRuntimeMappings = useMemo( - () => getCombinedRuntimeMappings(indexPattern, runtimeMappings), - [indexPattern, runtimeMappings] + () => getCombinedRuntimeMappings(dataView, runtimeMappings), + [dataView, runtimeMappings] ); // Available data grid columns, will be a combination of index pattern and runtime fields. const [columns, setColumns] = useState([]); useEffect(() => { - if (Array.isArray(indexPatternFields)) { + if (Array.isArray(dataViewFields)) { setColumns([ - ...getIndexPatternColumns(indexPattern, indexPatternFields), + ...getDataViewColumns(dataView, dataViewFields), ...(combinedRuntimeMappings ? getRuntimeFieldColumns(combinedRuntimeMappings) : []), ]); } - }, [indexPattern, indexPatternFields, combinedRuntimeMappings]); + }, [dataView, dataViewFields, combinedRuntimeMappings]); const dataGrid = useDataGrid(columns); @@ -175,19 +175,19 @@ export const useIndexData = ( setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); - const timeFieldName = indexPattern.getTimeField()?.name; + const timeFieldName = dataView.getTimeField()?.name; const sort: EsSorting = sortingColumns.reduce((s, column) => { s[column.id] = { order: column.direction }; return s; }, {} as EsSorting); const esSearchRequest = { - index: indexPattern.title, + index: dataView.title, body: { query, from: pagination.pageIndex * pagination.pageSize, size: pagination.pageSize, fields: [ - ...(indexPatternFields ?? []), + ...(dataViewFields ?? []), ...(isRuntimeMappings(combinedRuntimeMappings) ? Object.keys(combinedRuntimeMappings) : []), @@ -246,22 +246,22 @@ export const useIndexData = ( } } - if (indexPatternFields !== undefined && query !== undefined) { + if (dataViewFields !== undefined && query !== undefined) { fetchIndexData(); } // custom comparison // eslint-disable-next-line react-hooks/exhaustive-deps }, [ - indexPattern.title, - indexPatternFields, + dataView.title, + dataViewFields, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify([query, pagination, sortingColumns, combinedRuntimeMappings]), ]); const dataLoader = useMemo( - () => new DataLoader(indexPattern, toastNotifications), + () => new DataLoader(dataView, toastNotifications), // eslint-disable-next-line react-hooks/exhaustive-deps - [indexPattern] + [dataView] ); useEffect(() => { @@ -291,16 +291,16 @@ export const useIndexData = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, [ dataGrid.chartsVisible, - indexPattern.title, + dataView.title, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify([query, dataGrid.visibleColumns, runtimeMappings]), ]); - const renderCellValue = useRenderCellValue(indexPattern, pagination, tableItems); + const renderCellValue = useRenderCellValue(dataView, pagination, tableItems); return { ...dataGrid, - indexPatternFields, + dataViewFields, renderCellValue, timeRangeMs, }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index_pattern_prompt.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/data_view_prompt.tsx similarity index 96% rename from x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index_pattern_prompt.tsx rename to x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/data_view_prompt.tsx index cd60be7290b96..922ef80b3d1f2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/index_pattern_prompt/index_pattern_prompt.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/data_view_prompt.tsx @@ -15,7 +15,7 @@ interface Props { destIndex?: string; } -export const IndexPatternPrompt: FC = ({ destIndex, color }) => { +export const DataViewPrompt: FC = ({ destIndex, color }) => { const { services: { http: { basePath }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/index.ts new file mode 100644 index 0000000000000..4c5dbfd0a67b2 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/data_view_prompt/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DataViewPrompt } from './data_view_prompt'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx index 4e89a2a0833a1..d47dc0212f501 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx @@ -59,7 +59,7 @@ import { replaceStringTokens } from '../../../../../util/string_utils'; import { parseInterval } from '../../../../../../../common/util/parse_interval'; import { ExpandableSection, ExpandableSectionProps, HEADER_ITEMS_LOADING } from '.'; -import { IndexPatternPrompt } from '../index_pattern_prompt'; +import { DataViewPrompt } from '../data_view_prompt'; const showingDocs = i18n.translate( 'xpack.ml.dataframe.analytics.explorationResults.documentsShownHelpText', @@ -121,9 +121,9 @@ const getResultsSectionHeaderItems = ( interface ExpandableSectionResultsProps { colorRange?: ReturnType; indexData: UseIndexDataReturnType; - indexPattern?: DataView; + dataView?: DataView; jobConfig?: DataFrameAnalyticsConfig; - needsDestIndexPattern: boolean; + needsDestDataView: boolean; resultsField?: string; searchQuery: estypes.QueryDslQueryContainer; } @@ -131,9 +131,9 @@ interface ExpandableSectionResultsProps { export const ExpandableSectionResults: FC = ({ colorRange, indexData, - indexPattern, + dataView, jobConfig, - needsDestIndexPattern, + needsDestDataView, resultsField, searchQuery, }) => { @@ -146,7 +146,7 @@ export const ExpandableSectionResults: FC = ({ }, } = useMlKibana(); - const dataViewId = indexPattern?.id; + const dataViewId = dataView?.id; const discoverLocator = useMemo( () => share.url.locators.get('DISCOVER_APP_LOCATOR'), @@ -206,7 +206,7 @@ export const ExpandableSectionResults: FC = ({ if (discoverLocator !== undefined) { const url = await discoverLocator.getRedirectUrl({ - indexPatternId: dataViewId, + dataViewId, timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), query: { @@ -239,7 +239,7 @@ export const ExpandableSectionResults: FC = ({ if (timeRangeInterval !== null) { // Create a copy of the record as we are adding properties into it. const record = cloneDeep(item); - const timestamp = record[indexPattern!.timeFieldName!]; + const timestamp = record[dataView!.timeFieldName!]; const configuredUrlValue = customUrl.url_value; if (configuredUrlValue.includes('$earliest$')) { @@ -373,9 +373,9 @@ export const ExpandableSectionResults: FC = ({ const resultsSectionContent = ( <> - {jobConfig !== undefined && needsDestIndexPattern && ( + {jobConfig !== undefined && needsDestDataView && (
    - +
    )} {jobConfig !== undefined && @@ -386,7 +386,7 @@ export const ExpandableSectionResults: FC = ({ )} {(columnsWithCharts.length > 0 || searchQuery !== defaultSearchQuery) && - indexPattern !== undefined && ( + dataView !== undefined && ( <> {columnsWithCharts.length > 0 && (tableItems.length > 0 || status === INDEX_STATUS.LOADED) && ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx index 348c7051993a7..bd5cd677a7141 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx @@ -35,7 +35,7 @@ import { LoadingPanel } from '../loading_panel'; import { FeatureImportanceSummaryPanelProps } from '../total_feature_importance_summary/feature_importance_summary'; import { useExplorationUrlState } from '../../hooks/use_exploration_url_state'; import { ExplorationQueryBarProps } from '../exploration_query_bar/exploration_query_bar'; -import { IndexPatternPrompt } from '../index_pattern_prompt'; +import { DataViewPrompt } from '../data_view_prompt'; function getFilters(resultsField: string) { return { @@ -84,15 +84,15 @@ export const ExplorationPageWrapper: FC = ({ FeatureImportanceSummaryPanel, }) => { const { - indexPattern, - indexPatternErrorMessage, + dataView, + dataViewErrorMessage, isInitialized, isLoadingJobConfig, jobCapsServiceErrorMessage, jobConfig, jobConfigErrorMessage, jobStatus, - needsDestIndexPattern, + needsDestDataView, totalFeatureImportance, } = useResultsViewConfig(jobId); @@ -121,13 +121,13 @@ export const ExplorationPageWrapper: FC = ({ const destIndex = getDestinationIndex(jobConfig); const scatterplotFieldOptions = useScatterplotFieldOptions( - indexPattern, + dataView, jobConfig?.analyzed_fields?.includes, jobConfig?.analyzed_fields?.excludes, resultsField ); - if (indexPatternErrorMessage !== undefined) { + if (dataViewErrorMessage !== undefined) { return ( = ({ iconType="cross" >

    - {indexPatternErrorMessage} - {needsDestIndexPattern ? ( - - ) : null} + {dataViewErrorMessage} + {needsDestDataView ? : null}

    @@ -170,7 +168,7 @@ export const ExplorationPageWrapper: FC = ({ )} - {indexPattern !== undefined && jobConfig && ( + {dataView !== undefined && jobConfig && ( <> @@ -178,7 +176,7 @@ export const ExplorationPageWrapper: FC = ({ = ({ = ({ {isLoadingJobConfig === true && jobConfig === undefined && } {isLoadingJobConfig === false && jobConfig !== undefined && - indexPattern !== undefined && + dataView !== undefined && isInitialized === true && ( )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx index e2998651f2c21..e1efd592e956e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx @@ -25,7 +25,7 @@ import { removeFilterFromQueryString } from '../../../../../explorer/explorer_ut import { useMlKibana } from '../../../../../contexts/kibana'; export interface ExplorationQueryBarProps { - indexPattern: DataView; + dataView: DataView; setSearchQuery: (update: { queryString: string; query?: estypes.QueryDslQueryContainer; @@ -41,7 +41,7 @@ export interface ExplorationQueryBarProps { } export const ExplorationQueryBar: FC = ({ - indexPattern, + dataView, setSearchQuery, filters, query, @@ -99,7 +99,7 @@ export const ExplorationQueryBar: FC = ({ case SEARCH_QUERY_LANGUAGE.KUERY: convertedQuery = toElasticsearchQuery( fromKueryExpression(query.query as string), - indexPattern + dataView ); break; case SEARCH_QUERY_LANGUAGE.LUCENE: @@ -181,7 +181,7 @@ export const ExplorationQueryBar: FC = ({ = React.memo( - ({ indexPattern, jobConfig, needsDestIndexPattern, searchQuery }) => { + ({ dataView, jobConfig, needsDestDataView, searchQuery }) => { const { services: { mlServices: { mlApiServices }, @@ -39,7 +39,7 @@ export const ExplorationResultsTable: FC = React.memo( } = useMlKibana(); const classificationData = useExplorationResults( - indexPattern, + dataView, jobConfig, searchQuery, getToastNotifications(), @@ -54,10 +54,10 @@ export const ExplorationResultsTable: FC = React.memo(
    diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts index c7216037241d3..0a5dd784a6496 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts @@ -43,7 +43,7 @@ import { useTrainedModelsApiService } from '../../../../../services/ml_api_servi import { useExplorationDataGrid } from './use_exploration_data_grid'; export const useExplorationResults = ( - indexPattern: DataView | undefined, + dataView: DataView | undefined, jobConfig: DataFrameAnalyticsConfig | undefined, searchQuery: estypes.QueryDslQueryContainer, toastNotifications: CoreSetup['notifications']['toasts'], @@ -54,7 +54,7 @@ export const useExplorationResults = ( const trainedModelsApiService = useTrainedModelsApiService(); const needsDestIndexFields = - indexPattern !== undefined && indexPattern.title === jobConfig?.source.index[0]; + dataView !== undefined && dataView.title === jobConfig?.source.index[0]; const columns: EuiDataGridColumn[] = []; @@ -90,10 +90,9 @@ export const useExplorationResults = ( }, [jobConfig && jobConfig.id, dataGrid.pagination, searchQuery, dataGrid.sortingColumns]); const dataLoader = useMemo( - () => - indexPattern !== undefined ? new DataLoader(indexPattern, toastNotifications) : undefined, + () => (dataView !== undefined ? new DataLoader(dataView, toastNotifications) : undefined), // eslint-disable-next-line react-hooks/exhaustive-deps - [indexPattern] + [dataView] ); const fetchColumnChartsData = async function () { @@ -179,7 +178,7 @@ export const useExplorationResults = ( const resultsField = jobConfig?.dest.results_field ?? DEFAULT_RESULTS_FIELD; const renderCellValue = useRenderCellValue( - indexPattern, + dataView, dataGrid.pagination, dataGrid.tableItems, resultsField diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/job_config_error_callout/job_config_error_callout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/job_config_error_callout/job_config_error_callout.tsx index 9fc517d293e7f..9cbca46a03c8f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/job_config_error_callout/job_config_error_callout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/job_config_error_callout/job_config_error_callout.tsx @@ -37,7 +37,7 @@ export const JobConfigErrorCallout: FC = ({ application: { getUrlForApp }, }, } = useMlKibana(); - const containsIndexPatternLink = + const containsDataViewLink = typeof jobCapsServiceErrorMessage === 'string' && jobCapsServiceErrorMessage.includes('locate that index-pattern') && jobCapsServiceErrorMessage.includes('click here to re-create'); @@ -45,7 +45,7 @@ export const JobConfigErrorCallout: FC = ({ const message = (

    {jobConfigErrorMessage ? jobConfigErrorMessage : jobCapsServiceErrorMessage}

    ); - const newIndexPatternUrl = useMemo( + const newDataViewUrl = useMemo( () => getUrlForApp('management', { path: 'kibana/indexPatterns', @@ -54,8 +54,8 @@ export const JobConfigErrorCallout: FC = ({ [] ); - const calloutBody = containsIndexPatternLink ? ( - + const calloutBody = containsDataViewLink ? ( + {message} ) : ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx index f8fa311cb8a2a..8259ec4188c3e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx @@ -33,7 +33,7 @@ import { getFeatureCount } from './common'; import { useOutlierData } from './use_outlier_data'; import { useExplorationUrlState } from '../../hooks/use_exploration_url_state'; import { ExplorationQueryBarProps } from '../exploration_query_bar/exploration_query_bar'; -import { IndexPatternPrompt } from '../index_pattern_prompt'; +import { DataViewPrompt } from '../data_view_prompt'; export type TableItem = Record; @@ -42,12 +42,12 @@ interface ExplorationProps { } export const OutlierExploration: FC = React.memo(({ jobId }) => { - const { indexPattern, indexPatternErrorMessage, jobConfig, needsDestIndexPattern } = + const { dataView, dataViewErrorMessage, jobConfig, needsDestDataView } = useResultsViewConfig(jobId); const [pageUrlState, setPageUrlState] = useExplorationUrlState(); const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); - const outlierData = useOutlierData(indexPattern, jobConfig, searchQuery); + const outlierData = useOutlierData(dataView, jobConfig, searchQuery); const searchQueryUpdateHandler: ExplorationQueryBarProps['setSearchQuery'] = useCallback( (update) => { @@ -81,20 +81,20 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = // If feature influence was enabled for the legacy job we'll show a callout // with some additional information for a workaround. const showLegacyFeatureInfluenceFormatCallout = - !needsDestIndexPattern && + !needsDestDataView && isOutlierAnalysis(jobConfig?.analysis) && jobConfig?.analysis.outlier_detection.compute_feature_influence === true && columnsWithCharts.findIndex((d) => d.id === `${resultsField}.${FEATURE_INFLUENCE}`) === -1; const scatterplotFieldOptions = useScatterplotFieldOptions( - indexPattern, + dataView, jobConfig?.analyzed_fields?.includes, jobConfig?.analyzed_fields?.excludes, resultsField ); const destIndex = getDestinationIndex(jobConfig); - if (indexPatternErrorMessage !== undefined) { + if (dataViewErrorMessage !== undefined) { return ( = React.memo(({ jobId }) = iconType="cross" >

    - {indexPatternErrorMessage} - {needsDestIndexPattern ? ( - - ) : null} + {dataViewErrorMessage} + {needsDestDataView ? : null}

    @@ -124,10 +122,10 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = )} {(columnsWithCharts.length > 0 || searchQuery !== defaultSearchQuery) && - indexPattern !== undefined && ( + dataView !== undefined && ( <> @@ -165,9 +163,9 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = showColorRange && !showLegacyFeatureInfluenceFormatCallout ? colorRange : undefined } indexData={outlierData} - indexPattern={indexPattern} + dataView={dataView} jobConfig={jobConfig} - needsDestIndexPattern={needsDestIndexPattern} + needsDestDataView={needsDestDataView} searchQuery={searchQuery} /> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts index 73d88ecff6a23..4be3120569923 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts @@ -41,17 +41,17 @@ import { getFeatureCount, getOutlierScoreFieldName } from './common'; import { useExplorationDataGrid } from '../exploration_results_table/use_exploration_data_grid'; export const useOutlierData = ( - indexPattern: DataView | undefined, + dataView: DataView | undefined, jobConfig: DataFrameAnalyticsConfig | undefined, searchQuery: estypes.QueryDslQueryContainer ): UseIndexDataReturnType => { const needsDestIndexFields = - indexPattern !== undefined && indexPattern.title === jobConfig?.source.index[0]; + dataView !== undefined && dataView.title === jobConfig?.source.index[0]; const columns = useMemo(() => { const newColumns: EuiDataGridColumn[] = []; - if (jobConfig !== undefined && indexPattern !== undefined) { + if (jobConfig !== undefined && dataView !== undefined) { const resultsField = jobConfig.dest.results_field; const { fieldTypes } = getIndexFields(jobConfig, needsDestIndexFields); newColumns.push( @@ -63,7 +63,7 @@ export const useOutlierData = ( return newColumns; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [jobConfig, indexPattern]); + }, [jobConfig, dataView]); const dataGrid = useExplorationDataGrid( columns, @@ -95,11 +95,8 @@ export const useOutlierData = ( }, [jobConfig && jobConfig.id, dataGrid.pagination, searchQuery, dataGrid.sortingColumns]); const dataLoader = useMemo( - () => - indexPattern !== undefined - ? new DataLoader(indexPattern, getToastNotifications()) - : undefined, - [indexPattern] + () => (dataView !== undefined ? new DataLoader(dataView, getToastNotifications()) : undefined), + [dataView] ); const fetchColumnChartsData = async function () { @@ -146,7 +143,7 @@ export const useOutlierData = ( ); const renderCellValue = useRenderCellValue( - indexPattern, + dataView, dataGrid.pagination, dataGrid.tableItems, jobConfig?.dest.results_field ?? DEFAULT_RESULTS_FIELD, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx index 3d22ed72487f5..d18c43cda549b 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx @@ -21,12 +21,12 @@ export const DeleteActionModal: FC = ({ closeModal, deleteAndCloseModal, deleteTargetIndex, - deleteIndexPattern, - indexPatternExists, + deleteDataView, + dataViewExists, isLoading, item, toggleDeleteIndex, - toggleDeleteIndexPattern, + toggleDeleteDataView, userCanDeleteIndex, userCanDeleteDataView, }) => { @@ -77,15 +77,15 @@ export const DeleteActionModal: FC = ({ )}
    - {userCanDeleteIndex && indexPatternExists && ( + {userCanDeleteIndex && dataViewExists && ( )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx index d9a888b9e3147..03364165095d2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx @@ -99,7 +99,7 @@ describe('DeleteAction', () => { fireEvent.click(deleteButton); expect(getByTestId('mlAnalyticsJobDeleteModal')).toBeInTheDocument(); expect(queryByTestId('mlAnalyticsJobDeleteIndexSwitch')).toBeNull(); - expect(queryByTestId('mlAnalyticsJobDeleteIndexPatternSwitch')).toBeNull(); + expect(queryByTestId('mlAnalyticsJobDeleteDataViewSwitch')).toBeNull(); mock.mockRestore(); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index 1a828f6b6cdf0..f4c2773cd42f1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -42,10 +42,10 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { const [isDeleteJobCheckModalVisible, setDeleteJobCheckModalVisible] = useState(false); const [deleteItem, setDeleteItem] = useState(false); const [deleteTargetIndex, setDeleteTargetIndex] = useState(true); - const [deleteIndexPattern, setDeleteIndexPattern] = useState(true); + const [deleteDataView, setDeleteDataView] = useState(true); const [userCanDeleteIndex, setUserCanDeleteIndex] = useState(false); const [userCanDeleteDataView, setUserCanDeleteDataView] = useState(false); - const [indexPatternExists, setIndexPatternExists] = useState(false); + const [dataViewExists, setDataViewExists] = useState(false); const [isLoading, setIsLoading] = useState(false); const { @@ -57,13 +57,13 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { const toastNotificationService = useToastNotificationService(); - const checkIndexPatternExists = async () => { + const checkDataViewExists = async () => { try { const dv = (await dataViews.getIdsWithTitle()).find(({ title }) => title === indexName); if (dv !== undefined) { - setIndexPatternExists(true); + setDataViewExists(true); } else { - setIndexPatternExists(false); + setDataViewExists(false); } setIsLoading(false); } catch (e) { @@ -93,7 +93,7 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { capabilities.indexPatterns.save === true; setUserCanDeleteDataView(canDeleteDataView); if (canDeleteDataView === false) { - setDeleteIndexPattern(false); + setDeleteDataView(false); } } catch (e) { const error = extractErrorMessage(e); @@ -116,7 +116,7 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { setIsLoading(true); // Check if a data view exists corresponding to current DFA job // if data view does exist, show it to user - checkIndexPatternExists(); + checkDataViewExists(); // Check if an user has permission to delete the index & data view checkUserIndexPermission(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -129,12 +129,12 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { setModalVisible(false); if (item !== undefined) { - if ((userCanDeleteIndex && deleteTargetIndex) || (userCanDeleteIndex && deleteIndexPattern)) { + if ((userCanDeleteIndex && deleteTargetIndex) || (userCanDeleteIndex && deleteDataView)) { deleteAnalyticsAndDestIndex( item.config, item.stats, deleteTargetIndex, - indexPatternExists && deleteIndexPattern, + dataViewExists && deleteDataView, toastNotificationService ); } else { @@ -143,7 +143,7 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { } }; const toggleDeleteIndex = () => setDeleteTargetIndex(!deleteTargetIndex); - const toggleDeleteIndexPattern = () => setDeleteIndexPattern(!deleteIndexPattern); + const toggleDeleteDataView = () => setDeleteDataView(!deleteDataView); const openModal = (newItem: DataFrameAnalyticsListRowEssentials) => { setItem(newItem); @@ -181,9 +181,9 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { closeModal, deleteAndCloseModal, deleteTargetIndex, - deleteIndexPattern, + deleteDataView, deleteItem, - indexPatternExists, + dataViewExists, isDeleteJobCheckModalVisible, isModalVisible, isLoading, @@ -192,7 +192,7 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { openModal, openDeleteJobCheckModal, toggleDeleteIndex, - toggleDeleteIndexPattern, + toggleDeleteDataView, userCanDeleteIndex, userCanDeleteDataView, }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx index 89c16fbb93050..9525042f6a47a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx @@ -146,7 +146,7 @@ export const SourceSelection: FC = () => { type: 'index-pattern', getIconForSavedObject: () => 'indexPatternApp', name: i18n.translate( - 'xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.indexPattern', + 'xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.dataView', { defaultMessage: 'Data view', } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts index b9b8de0ce0d55..a1dffe771d8d2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts @@ -18,7 +18,7 @@ export enum ACTION { RESET_FORM, SET_ADVANCED_EDITOR_RAW_STRING, SET_FORM_STATE, - SET_INDEX_PATTERN_TITLES, + SET_DATA_VIEW_TITLES, SET_IS_JOB_CREATED, SET_IS_JOB_STARTED, SET_IS_MODAL_BUTTON_DISABLED, @@ -51,9 +51,9 @@ export type Action = } | { type: ACTION.SET_FORM_STATE; payload: Partial } | { - type: ACTION.SET_INDEX_PATTERN_TITLES; + type: ACTION.SET_DATA_VIEW_TITLES; payload: { - indexPatternsMap: SourceIndexMap; + dataViewsMap: SourceIndexMap; }; } | { type: ACTION.SET_IS_JOB_CREATED; isJobCreated: State['isJobCreated'] } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts index 1a5beada2c3c0..5f02d7bbbb4fd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.test.ts @@ -39,7 +39,7 @@ const getMockState = ({ jobIdEmpty: false, jobIdValid: true, jobIdExists: false, - createIndexPattern: false, + createDataView: false, }, jobConfig: { source: { index }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts index 69eececeba129..d2689a009fb1b 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts @@ -148,7 +148,7 @@ export const validateNumTopFeatureImportanceValues = ( }; export const validateAdvancedEditor = (state: State): State => { - const { jobIdEmpty, jobIdValid, jobIdExists, jobType, createIndexPattern } = state.form; + const { jobIdEmpty, jobIdValid, jobIdExists, jobType, createDataView } = state.form; const { jobConfig } = state; state.advancedEditorMessages = []; @@ -161,8 +161,7 @@ export const validateAdvancedEditor = (state: State): State => { const destinationIndexName = jobConfig?.dest?.index ?? ''; const destinationIndexNameEmpty = destinationIndexName === ''; const destinationIndexNameValid = isValidIndexName(destinationIndexName); - const destinationIndexPatternTitleExists = - state.indexPatternsMap[destinationIndexName] !== undefined; + const destinationDataViewTitleExists = state.dataViewsMap[destinationIndexName] !== undefined; const analyzedFields = jobConfig?.analyzed_fields?.includes || []; @@ -294,7 +293,7 @@ export const validateAdvancedEditor = (state: State): State => { ), message: '', }); - } else if (destinationIndexPatternTitleExists && !createIndexPattern) { + } else if (destinationDataViewTitleExists && !createDataView) { state.advancedEditorMessages.push({ error: i18n.translate( 'xpack.ml.dataframe.analytics.create.advancedEditorMessage.destinationIndexNameExistsWarn', @@ -360,7 +359,7 @@ export const validateAdvancedEditor = (state: State): State => { }); } - state.form.destinationIndexPatternTitleExists = destinationIndexPatternTitleExists; + state.form.destinationDataViewTitleExists = destinationDataViewTitleExists; state.isValid = includesValid && @@ -377,7 +376,7 @@ export const validateAdvancedEditor = (state: State): State => { !dependentVariableEmpty && !modelMemoryLimitEmpty && (numTopFeatureImportanceValuesValid || jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION) && - (!destinationIndexPatternTitleExists || !createIndexPattern); + (!destinationDataViewTitleExists || !createDataView); return state; }; @@ -425,8 +424,8 @@ const validateForm = (state: State): State => { sourceIndexNameValid, destinationIndexNameEmpty, destinationIndexNameValid, - destinationIndexPatternTitleExists, - createIndexPattern, + destinationDataViewTitleExists, + createDataView, dependentVariable, modelMemoryLimit, numTopFeatureImportanceValuesValid, @@ -458,7 +457,7 @@ const validateForm = (state: State): State => { destinationIndexNameValid && !dependentVariableEmpty && (numTopFeatureImportanceValuesValid || jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION) && - (!destinationIndexPatternTitleExists || !createIndexPattern); + (!destinationDataViewTitleExists || !createDataView); return state; }; @@ -513,8 +512,8 @@ export function reducer(state: State, action: Action): State { if (action.payload.destinationIndex !== undefined) { newFormState.destinationIndexNameEmpty = newFormState.destinationIndex === ''; newFormState.destinationIndexNameValid = isValidIndexName(newFormState.destinationIndex); - newFormState.destinationIndexPatternTitleExists = - state.indexPatternsMap[newFormState.destinationIndex] !== undefined; + newFormState.destinationDataViewTitleExists = + state.dataViewsMap[newFormState.destinationIndex] !== undefined; } if (action.payload.jobId !== undefined) { @@ -541,13 +540,13 @@ export function reducer(state: State, action: Action): State { ? validateAdvancedEditor({ ...state, form: newFormState }) : validateForm({ ...state, form: newFormState }); - case ACTION.SET_INDEX_PATTERN_TITLES: { + case ACTION.SET_DATA_VIEW_TITLES: { const newState = { ...state, ...action.payload, }; - newState.form.destinationIndexPatternTitleExists = - newState.indexPatternsMap[newState.form.destinationIndex] !== undefined; + newState.form.destinationDataViewTitleExists = + newState.dataViewsMap[newState.form.destinationIndex] !== undefined; return newState; } @@ -591,8 +590,8 @@ export function reducer(state: State, action: Action): State { formState.destinationIndexNameEmpty = formState.destinationIndex === ''; formState.destinationIndexNameValid = isValidIndexName(formState.destinationIndex || ''); - formState.destinationIndexPatternTitleExists = - state.indexPatternsMap[formState.destinationIndex || ''] !== undefined; + formState.destinationDataViewTitleExists = + state.dataViewsMap[formState.destinationIndex || ''] !== undefined; if (formState.numTopFeatureImportanceValues !== undefined) { formState.numTopFeatureImportanceValuesValid = validateNumTopFeatureImportanceValues( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index d567c7d19b3d3..efffc221c3174 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -35,13 +35,10 @@ export const UNSET_CONFIG_ITEM = '--'; export type EsIndexName = string; export type DependentVariable = string; -export type IndexPatternTitle = string; +export type DataViewTitle = string; export type AnalyticsJobType = DataFrameAnalysisConfigType | undefined; -type IndexPatternId = string; -export type SourceIndexMap = Record< - IndexPatternTitle, - { label: IndexPatternTitle; value: IndexPatternId } ->; +type DataViewId = string; +export type SourceIndexMap = Record; export interface FormMessage { error?: string; @@ -55,7 +52,7 @@ export interface State { form: { alpha: undefined | number; computeFeatureInfluence: string; - createIndexPattern: boolean; + createDataView: boolean; classAssignmentObjective: undefined | string; dependentVariable: DependentVariable; description: string; @@ -63,7 +60,7 @@ export interface State { destinationIndexNameExists: boolean; destinationIndexNameEmpty: boolean; destinationIndexNameValid: boolean; - destinationIndexPatternTitleExists: boolean; + destinationDataViewTitleExists: boolean; downsampleFactor: undefined | number; earlyStoppingEnabled: undefined | boolean; eta: undefined | number; @@ -120,7 +117,7 @@ export interface State { useEstimatedMml: boolean; }; disabled: boolean; - indexPatternsMap: SourceIndexMap; + dataViewsMap: SourceIndexMap; isAdvancedEditorEnabled: boolean; isAdvancedEditorValidJson: boolean; hasSwitchedToEditor: boolean; @@ -141,7 +138,7 @@ export const getInitialState = (): State => ({ form: { alpha: undefined, computeFeatureInfluence: 'true', - createIndexPattern: true, + createDataView: true, classAssignmentObjective: undefined, dependentVariable: '', description: '', @@ -149,7 +146,7 @@ export const getInitialState = (): State => ({ destinationIndexNameExists: false, destinationIndexNameEmpty: true, destinationIndexNameValid: false, - destinationIndexPatternTitleExists: false, + destinationDataViewTitleExists: false, earlyStoppingEnabled: undefined, downsampleFactor: undefined, eta: undefined, @@ -210,7 +207,7 @@ export const getInitialState = (): State => ({ !mlNodesAvailable() || !checkPermission('canCreateDataFrameAnalytics') || !checkPermission('canStartStopDataFrameAnalytics'), - indexPatternsMap: {}, + dataViewsMap: {}, isAdvancedEditorEnabled: false, isAdvancedEditorValidJson: true, hasSwitchedToEditor: false, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index e7b618808100b..c097577bc289a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -18,7 +18,6 @@ import { ml } from '../../../../../services/ml_api_service'; import { useRefreshAnalyticsList } from '../../../../common'; import { extractCloningConfig, isAdvancedConfig } from '../../components/action_clone'; -import { createKibanaDataView } from '../../../../../components/ml_inference/retry_create_data_view'; import { ActionDispatchers, ACTION } from './actions'; import { reducer } from './reducer'; @@ -59,8 +58,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const { refresh } = useRefreshAnalyticsList(); const { form, jobConfig, isAdvancedEditorEnabled } = state; - const { createIndexPattern, jobId } = form; - let { destinationIndex } = form; + const { createDataView, jobId } = form; const addRequestMessage = (requestMessage: FormMessage) => dispatch({ type: ACTION.ADD_REQUEST_MESSAGE, requestMessage }); @@ -73,8 +71,8 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const setAdvancedEditorRawString = (advancedEditorRawString: string) => dispatch({ type: ACTION.SET_ADVANCED_EDITOR_RAW_STRING, advancedEditorRawString }); - const setIndexPatternTitles = (payload: { indexPatternsMap: SourceIndexMap }) => - dispatch({ type: ACTION.SET_INDEX_PATTERN_TITLES, payload }); + const setDataViewTitles = (payload: { dataViewsMap: SourceIndexMap }) => + dispatch({ type: ACTION.SET_DATA_VIEW_TITLES, payload }); const setIsJobCreated = (isJobCreated: boolean) => dispatch({ type: ACTION.SET_IS_JOB_CREATED, isJobCreated }); @@ -94,12 +92,13 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { isAdvancedEditorEnabled ? jobConfig : getJobConfigFromFormState(form) ) as DataFrameAnalyticsConfig; - if (isAdvancedEditorEnabled) { - destinationIndex = analyticsJobConfig.dest.index; - } - try { - await ml.dataFrameAnalytics.createDataFrameAnalytics(jobId, analyticsJobConfig); + await ml.dataFrameAnalytics.createDataFrameAnalytics( + jobId, + analyticsJobConfig, + createDataView, + form.timeFieldName + ); addRequestMessage({ message: i18n.translate( 'xpack.ml.dataframe.stepCreateForm.createDataFrameAnalyticsSuccessMessage', @@ -110,9 +109,6 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { ), }); setIsJobCreated(true); - if (createIndexPattern) { - createKibanaDataView(destinationIndex, dataViews, form.timeFieldName, addRequestMessage); - } refresh(); return true; } catch (e) { @@ -132,17 +128,17 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const prepareFormValidation = async () => { try { // Set the existing data view names. - const indexPatternsMap: SourceIndexMap = {}; + const dataViewsMap: SourceIndexMap = {}; const savedObjects = (await dataViews.getCache()) || []; savedObjects.forEach((obj) => { const title = obj?.attributes?.title; if (title !== undefined) { const id = obj?.id || ''; - indexPatternsMap[title] = { label: title, value: id }; + dataViewsMap[title] = { label: title, value: id }; } }); - setIndexPatternTitles({ - indexPatternsMap, + setDataViewTitles({ + dataViewsMap, }); } catch (e) { addRequestMessage({ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 8929f39bea43c..ea3c24884bbec 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -48,7 +48,7 @@ export const deleteAnalyticsAndDestIndex = async ( analyticsConfig: DataFrameAnalyticsListRow['config'], analyticsStats: DataFrameAnalyticsListRow['stats'], deleteDestIndex: boolean, - deleteDestIndexPattern: boolean, + deleteDestDataView: boolean, toastNotificationService: ToastNotificationService ) => { const destinationIndex = analyticsConfig.dest.index; @@ -59,7 +59,7 @@ export const deleteAnalyticsAndDestIndex = async ( const status = await ml.dataFrameAnalytics.deleteDataFrameAnalyticsAndDestIndex( analyticsConfig.id, deleteDestIndex, - deleteDestIndexPattern + deleteDestDataView ); if (status.analyticsJobDeleted?.success) { toastNotificationService.displaySuccessToast( @@ -97,7 +97,7 @@ export const deleteAnalyticsAndDestIndex = async ( ); } - if (status.destIndexPatternDeleted?.success) { + if (status.destDataViewDeleted?.success) { toastNotificationService.displaySuccessToast( i18n.translate( 'xpack.ml.dataframe.analyticsList.deleteAnalyticsWithDataViewSuccessMessage', @@ -108,8 +108,8 @@ export const deleteAnalyticsAndDestIndex = async ( ) ); } - if (status.destIndexPatternDeleted?.error) { - const error = extractErrorMessage(status.destIndexPatternDeleted.error); + if (status.destDataViewDeleted?.error) { + const error = extractErrorMessage(status.destDataViewDeleted.error); toastNotificationService.displayDangerToast( i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsWithDataViewErrorMessage', { defaultMessage: 'An error occurred deleting data view {destinationIndex}: {error}', diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/_legend.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/_legend.scss index 5a3af985446c8..7e7168595a44e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/_legend.scss +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/_legend.scss @@ -37,6 +37,15 @@ display: 'inline-block'; } +.mlJobMapLegend__analyticsMissing { + height: $euiSizeM; + width: $euiSizeM; + background-color: $euiColorGhost; + border: $euiBorderWidthThick solid $euiColorFullShade; + border-radius: 50%; + display: 'inline-block'; +} + .mlJobMapLegend__sourceNode { height: $euiSizeM; width: $euiSizeM; @@ -44,4 +53,4 @@ border: $euiBorderThin; border-radius: $euiBorderRadius; display: 'inline-block'; -} +} \ No newline at end of file diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx index 82695b39e0066..2786046610fc7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/controls.tsx @@ -196,7 +196,17 @@ export const Controls: FC = React.memo( // Set up Cytoscape event handlers useEffect(() => { const selectHandler: cytoscape.EventHandler = (event) => { - setSelectedNode(event.target); + const targetNode = event.target; + if (targetNode._private.data.type === JOB_MAP_NODE_TYPES.ANALYTICS_JOB_MISSING) { + toasts.addWarning( + i18n.translate('xpack.ml.dataframe.analyticsMap.flyout.jobMissingMessage', { + defaultMessage: 'There is no data available for job {label}.', + values: { label: targetNode._private.data.label }, + }) + ); + return; + } + setSelectedNode(targetNode); setShowFlyout(true); }; @@ -211,7 +221,7 @@ export const Controls: FC = React.memo( cy.removeListener('unselect', 'node', deselect); } }; - }, [cy, deselect]); + }, [cy, deselect, toasts]); useEffect( function updateElementsOnClose() { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/cytoscape_options.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/cytoscape_options.tsx index 9cdd41dfb9c88..b7ca710fb43f3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/cytoscape_options.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/cytoscape_options.tsx @@ -28,6 +28,8 @@ function shapeForNode(el: cytoscape.NodeSingular, theme: EuiThemeType): MapShape switch (type) { case JOB_MAP_NODE_TYPES.ANALYTICS: return MAP_SHAPES.ELLIPSE; + case JOB_MAP_NODE_TYPES.ANALYTICS_JOB_MISSING: + return MAP_SHAPES.ELLIPSE; case JOB_MAP_NODE_TYPES.TRANSFORM: return MAP_SHAPES.RECTANGLE; case JOB_MAP_NODE_TYPES.INDEX: @@ -65,6 +67,8 @@ function borderColorForNode(el: cytoscape.NodeSingular, theme: EuiThemeType) { const type = el.data('type'); switch (type) { + case JOB_MAP_NODE_TYPES.ANALYTICS_JOB_MISSING: + return theme.euiColorFullShade; case JOB_MAP_NODE_TYPES.ANALYTICS: return theme.euiColorSuccess; case JOB_MAP_NODE_TYPES.TRANSFORM: diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/legend.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/legend.tsx index 9494f7fc08799..53379c4809d9a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/legend.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/components/legend.tsx @@ -32,7 +32,10 @@ const getJobTypeList = () => ( ); -export const JobMapLegend: FC<{ theme: EuiThemeType }> = ({ theme }) => { +export const JobMapLegend: FC<{ hasMissingJobNode: boolean; theme: EuiThemeType }> = ({ + hasMissingJobNode, + theme, +}) => { const [showJobTypes, setShowJobTypes] = useState(false); return ( @@ -122,6 +125,23 @@ export const JobMapLegend: FC<{ theme: EuiThemeType }> = ({ theme }) => {
    + {hasMissingJobNode ? ( + + + + + + + + + + + + + ) : null} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/job_map.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/job_map.tsx index ae1adea958959..f4ba7e3a4c1d7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/job_map.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/job_map.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC, useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; @@ -150,6 +150,10 @@ export const JobMap: FC = ({ defaultHeight, analyticsId, modelId, forceRe const { ref, width, height } = useRefDimensions(); const refreshCallback = () => fetchAndSetElementsWrapper({ analyticsId, modelId }); + const hasMissingJobNode = useMemo( + () => elements.map(({ data }) => data.type).includes(JOB_MAP_NODE_TYPES.ANALYTICS_JOB_MISSING), + [elements] + ); const h = defaultHeight ?? height; return ( @@ -157,7 +161,7 @@ export const JobMap: FC = ({ defaultHeight, analyticsId, modelId, forceRe - + void; } /** @@ -187,6 +188,7 @@ export const SwimlaneContainer: FC = ({ showLegend = true, 'data-test-subj': dataTestSubj, yAxisWidth, + onRenderComplete, }) => { const [chartWidth, setChartWidth] = useState(0); @@ -407,6 +409,10 @@ export const SwimlaneContainer: FC = ({ const noSwimLaneData = !isLoading && !showSwimlane && !!noDataWarning; + if (noSwimLaneData) { + onRenderComplete?.(); + } + // A resize observer is required to compute the bucket span based on the chart width to fetch the data accordingly return ( @@ -453,6 +459,11 @@ export const SwimlaneContainer: FC = ({ debugState={window._echDebugStateFlag ?? false} onBrushEnd={onBrushEnd as BrushEndListener} locale={i18n.getLocale()} + onRenderChange={(isRendered) => { + if (isRendered && onRenderComplete) { + onRenderComplete(); + } + }} /> j.blocked !== undefined)) { + if ( + blockingJobsRefreshTimeout === null && + jobsSummaryList.some((j) => j.blocked !== undefined) + ) { // if there are some jobs in a deleting state, start polling for // deleting jobs so we can update the jobs list once the // deleting tasks are over diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx index 95ac0b4043f57..55692ac507195 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx @@ -23,11 +23,11 @@ import { KibanaObjectUi } from '../page'; export interface KibanaObjectItemProps { objectType: string; - kibanaObjects: KibanaObjectUi[]; + kibanaObjects: KibanaObjectUi[] | undefined; isSaving: boolean; } -export const KibanaObjects: FC = memo( +export const KibanaObjectList: FC = memo( ({ objectType, kibanaObjects, isSaving }) => { const kibanaObjectLabels: Record = { dashboard: i18n.translate('xpack.ml.newJob.recognize.dashboardsLabel', { @@ -41,6 +41,10 @@ export const KibanaObjects: FC = memo( }), }; + if (kibanaObjects === undefined) { + return null; + } + return ( <> @@ -53,7 +57,7 @@ export const KibanaObjects: FC = memo( - + {title} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx index 8be908871fe2c..fe2c5dfa966aa 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx @@ -30,11 +30,12 @@ import { JobOverride, JobResponse, KibanaObject, + KibanaObjects, KibanaObjectResponse, ModuleJob, } from '../../../../../common/types/modules'; import { CreateResultCallout } from './components/create_result_callout'; -import { KibanaObjects } from './components/kibana_objects'; +import { KibanaObjectList } from './components/kibana_objects'; import { ModuleJobs } from './components/module_jobs'; import { JobSettingsForm, JobSettingsFormValues } from './components/job_settings_form'; import { TimeRange } from '../common/components'; @@ -50,10 +51,6 @@ export interface ModuleJobUI extends ModuleJob { export type KibanaObjectUi = KibanaObject & KibanaObjectResponse; -export interface KibanaObjects { - [objectType: string]: KibanaObjectUi[]; -} - interface PageProps { moduleId: string; existingGroupIds: string[]; @@ -111,6 +108,7 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { try { const response = await getDataRecognizerModule({ moduleId }); setJobs(response.jobs); + setKibanaObjects(response.kibana); setSaveState(SAVE_STATE.NOT_SAVED); @@ -365,7 +363,7 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { {Object.keys(kibanaObjects).map((objectType, i) => ( - void; + onSubmit: (modelId: string) => void; +} + +/** + * Flyout for downloading elastic curated models and showing instructions for importing third-party models. + */ +export const AddModelFlyout: FC = ({ onClose, onSubmit, modelDownloads }) => { + const canCreateTrainedModels = usePermissionCheck('canCreateTrainedModels'); + const isElserTabVisible = canCreateTrainedModels && modelDownloads.length > 0; + + const [selectedTabId, setSelectedTabId] = useState(isElserTabVisible ? 'elser' : 'thirdParty'); + + const tabs = useMemo(() => { + return [ + ...(isElserTabVisible + ? [ + { + id: 'elser', + name: ( + + + + + + + + + ), + content: ( + + ), + }, + ] + : []), + { + id: 'thirdParty', + name: ( + + ), + content: , + }, + ]; + }, [isElserTabVisible, modelDownloads, onSubmit]); + + const selectedTabContent = useMemo(() => { + return tabs.find((obj) => obj.id === selectedTabId)?.content; + }, [selectedTabId, tabs]); + + return ( + + + +

    + +

    +
    + + {tabs.map((tab) => ( + + {tab.name} + + ))} + +
    + {selectedTabContent} + + + + + + + + + +
    + ); +}; + +interface ElserTabContentProps { + modelDownloads: ModelItem[]; + onModelDownload: (modelId: string) => void; +} + +/** + * ELSER tab content for selecting a model to download. + */ +const ElserTabContent: FC = ({ modelDownloads, onModelDownload }) => { + const { + services: { docLinks }, + } = useMlKibana(); + + const [selectedModelId, setSelectedModelId] = useState( + modelDownloads.find((m) => m.recommended)?.model_id + ); + + return ( + <> + {Object.entries(groupBy(modelDownloads, 'modelName')).map(([modelName, models]) => { + return ( + + {modelName === 'elser' ? ( +
    + +

    + +

    +
    + +

    + + + +

    + +

    + + + +

    + +
    + ) : null} + + + ), + }} + > + {models.map((model) => { + return ( + + + +
    + + + {model.os === 'Linux' && model.arch === 'amd64' ? ( + + ) : ( + + )} + + +
    + + {model.model_id} + +
    + {model.recommended ? ( + + + } + > + + + + + + ) : null} +
    + } + name={model.model_id} + value={model.model_id} + checked={model.model_id === selectedModelId} + onChange={setSelectedModelId.bind(null, model.model_id)} + /> + + + ); + })} + + + ); + })} + + + + + ); +}; + +/** + * Third-party tab content for showing instructions for importing third-party models. + */ +const ThirdPartyTabContent: FC = () => { + const { + services: { docLinks }, + } = useMlKibana(); + + return ( + <> + + +

    + + + pip + + ), + pypiLink: ( + + PyPI + + ), + }} + /> + +

    +

    + + $ python -m pip install eland + +

    +

    + + + Conda + + ), + condaForgeLink: ( + + Conda Forge + + ), + }} + /> + +

    +

    + + $ conda install -c conda-forge eland + +

    + + ), + }, + { + title: i18n.translate('xpack.ml.trainedModels.addModelFlyout.thirdParty.step2Title', { + defaultMessage: 'Importing your third-party model', + }), + children: ( + +

    + + + +

    + +

    + + + + + + eland_import_hub_model
    + --cloud-id <cloud-id> \
    + -u <username> -p <password> \
    + --hub-model-id <model-id> \
    + --task-type ner \ +
    +

    + + + + + + + + + + + + + +
    + ), + }, + { + title: i18n.translate('xpack.ml.trainedModels.addModelFlyout.thirdParty.step4Title', { + defaultMessage: 'Deploy your model', + }), + children: ( + <> + +

    + +

    +
    + + +

    + +

    +
    + + ), + }, + ]} + /> + + ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx b/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx index 3ac6960af55ca..102af34d3e95d 100644 --- a/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx +++ b/x-pack/plugins/ml/public/application/model_management/deployment_setup.tsx @@ -31,7 +31,7 @@ import type { I18nStart, OverlayStart, ThemeServiceStart } from '@kbn/core/publi import { css } from '@emotion/react'; import { numberValidator } from '@kbn/ml-agg-utils'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import { isCloudTrial } from '../services/ml_server_info'; +import { getNewJobLimits, isCloudTrial } from '../services/ml_server_info'; import { composeValidators, dictionaryValidator, @@ -42,7 +42,7 @@ import { ModelItem } from './models_list'; interface DeploymentSetupProps { config: ThreadingParams; onConfigChange: (config: ThreadingParams) => void; - errors: Partial>; + errors: Partial>>; isUpdate?: boolean; deploymentsParams?: Record; } @@ -66,6 +66,11 @@ export const DeploymentSetup: FC = ({ isUpdate, deploymentsParams, }) => { + const { + total_ml_processors: totalMlProcessors, + max_single_ml_node_processors: maxSingleMlNodeProcessors, + } = getNewJobLimits(); + const numOfAllocation = config.numOfAllocations; const threadsPerAllocations = config.threadsPerAllocations; @@ -76,17 +81,20 @@ export const DeploymentSetup: FC = ({ const threadsPerAllocationsOptions = useMemo( () => - new Array(THREADS_MAX_EXPONENT).fill(null).map((v, i) => { - const value = Math.pow(2, i); - const id = value.toString(); - - return { - id, - label: id, - value, - }; - }), - [] + new Array(THREADS_MAX_EXPONENT) + .fill(null) + .map((v, i) => Math.pow(2, i)) + .filter(maxSingleMlNodeProcessors ? (v) => v <= maxSingleMlNodeProcessors : (v) => true) + .map((value) => { + const id = value.toString(); + + return { + id, + label: id, + value, + }; + }), + [maxSingleMlNodeProcessors] ); const disableThreadingControls = config.priority === 'low'; @@ -251,11 +259,28 @@ export const DeploymentSetup: FC = ({ } hasChildLabel={false} isDisabled={disableThreadingControls} + isInvalid={!!errors.numOfAllocations} + error={ + errors?.numOfAllocations?.min ? ( + + ) : errors?.numOfAllocations?.max ? ( + + ) : null + } > = ({ }) => { const isUpdate = !!initialParams; + const { total_ml_processors: totalMlProcessors } = getNewJobLimits(); + const [config, setConfig] = useState( initialParams ?? { numOfAllocations: 1, @@ -373,7 +400,7 @@ export const StartUpdateDeploymentModal: FC = ({ const numOfAllocationsValidator = composeValidators( requiredValidator(), - numberValidator({ min: 1, integerOnly: true }) + numberValidator({ min: 1, max: totalMlProcessors, integerOnly: true }) ); const numOfAllocationsErrors = numOfAllocationsValidator(config.numOfAllocations); diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 42e095edeccf5..d91e944602d9b 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -42,12 +42,14 @@ export function useModelActions({ isLoading, fetchModels, modelAndDeploymentIds, + onModelDownloadRequest, }: { isLoading: boolean; onDfaTestAction: (model: ModelItem) => void; onTestAction: (model: ModelItem) => void; onModelsDeleteRequest: (models: ModelItem[]) => void; onModelDeployRequest: (model: ModelItem) => void; + onModelDownloadRequest: (modelId: string) => void; onLoading: (isLoading: boolean) => void; fetchModels: () => Promise; modelAndDeploymentIds: string[]; @@ -410,31 +412,7 @@ export function useModelActions({ item.state === MODEL_STATE.NOT_DOWNLOADED, enabled: (item) => !isLoading, onClick: async (item) => { - try { - onLoading(true); - await trainedModelsApiService.installElasticTrainedModelConfig(item.model_id); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.downloadSuccess', { - defaultMessage: '"{modelId}" model download has been started successfully.', - values: { - modelId: item.model_id, - }, - }) - ); - // Need to fetch model state updates - await fetchModels(); - } catch (e) { - displayErrorToast( - e, - i18n.translate('xpack.ml.trainedModels.modelsList.downloadFailed', { - defaultMessage: 'Failed to download "{modelId}"', - values: { - modelId: item.model_id, - }, - }) - ); - onLoading(false); - } + onModelDownloadRequest(item.model_id); }, }, { @@ -481,7 +459,7 @@ export function useModelActions({ ); }, enabled: (item) => { - return item.state !== MODEL_STATE.STARTED; + return canStartStopTrainedModels && item.state !== MODEL_STATE.STARTED; }, }, { @@ -614,6 +592,7 @@ export function useModelActions({ onTestAction, trainedModelsApiService, urlLocator, + onModelDownloadRequest, ] ); } diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index e9672729b6f4f..6d9dda39e2853 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -45,6 +45,7 @@ import { } from '@kbn/ml-trained-models-utils'; import { isDefined } from '@kbn/ml-is-defined'; import { useStorage } from '@kbn/ml-local-storage'; +import { AddModelFlyout } from './add_model_flyout'; import { getModelStateColor } from './get_model_state_color'; import { ML_ELSER_CALLOUT_DISMISSED } from '../../../common/types/storage'; import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; @@ -84,6 +85,12 @@ export type ModelItem = TrainedModelConfigResponse & { putModelConfig?: object; state: ModelState; recommended?: boolean; + /** + * Model name, e.g. elser + */ + modelName?: string; + os?: string; + arch?: string; }; export type ModelItemFull = Required; @@ -153,7 +160,7 @@ export const ModelsList: FC = ({ const trainedModelsApiService = useTrainedModelsApiService(); - const { displayErrorToast } = useToastNotificationService(); + const { displayErrorToast, displaySuccessToast } = useToastNotificationService(); const [isInitialized, setIsInitialized] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -166,6 +173,7 @@ export const ModelsList: FC = ({ ); const [modelToTest, setModelToTest] = useState(null); const [dfaModelToTest, setDfaModelToTest] = useState(null); + const [isAddModelFlyoutVisible, setIsAddModelFlyoutVisible] = useState(false); const isBuiltInModel = useCallback( (item: ModelItem) => item.tags.includes(BUILT_IN_MODEL_TAG), @@ -269,6 +277,9 @@ export const ModelsList: FC = ({ description: modelDefinition.description, state: MODEL_STATE.NOT_DOWNLOADED, recommended: !!modelDefinition.recommended, + modelName: modelDefinition.modelName, + os: modelDefinition.os, + arch: modelDefinition.arch, } as ModelItem; }); resultItems = [...resultItems, ...notDownloaded]; @@ -406,6 +417,33 @@ export const ModelsList: FC = ({ [existingModels] ); + const onModelDownloadRequest = useCallback( + async (modelId: string) => { + try { + setIsLoading(true); + await trainedModelsApiService.installElasticTrainedModelConfig(modelId); + displaySuccessToast( + i18n.translate('xpack.ml.trainedModels.modelsList.downloadSuccess', { + defaultMessage: '"{modelId}" model download has been started successfully.', + values: { modelId }, + }) + ); + // Need to fetch model state updates + await fetchModelsData(); + } catch (e) { + displayErrorToast( + e, + i18n.translate('xpack.ml.trainedModels.modelsList.downloadFailed', { + defaultMessage: 'Failed to download "{modelId}"', + values: { modelId }, + }) + ); + setIsLoading(true); + } + }, + [displayErrorToast, displaySuccessToast, fetchModelsData, trainedModelsApiService] + ); + /** * Table actions */ @@ -418,6 +456,7 @@ export const ModelsList: FC = ({ onModelDeployRequest: setModelToDeploy, onLoading: setIsLoading, modelAndDeploymentIds, + onModelDownloadRequest, }); const toggleDetails = async (item: ModelItem) => { @@ -689,13 +728,24 @@ export const ModelsList: FC = ({ <> - {modelsStats && ( - <> - - - - - )} + {modelsStats ? ( + + + + ) : null} + + + + +
    @@ -776,6 +826,16 @@ export const ModelsList: FC = ({ model={modelToDeploy} /> ) : null} + {isAddModelFlyoutVisible ? ( + i.state === MODEL_STATE.NOT_DOWNLOADED)} + onClose={setIsAddModelFlyoutVisible.bind(null, false)} + onSubmit={(modelId) => { + onModelDownloadRequest(modelId); + setIsAddModelFlyoutVisible(false); + }} + /> + ) : null} ); }; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts index 9d9d0f859655e..5794de5151e84 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts @@ -49,7 +49,7 @@ export interface DeleteDataFrameAnalyticsWithIndexResponse { acknowledged: boolean; analyticsJobDeleted: DeleteDataFrameAnalyticsWithIndexStatus; destIndexDeleted: DeleteDataFrameAnalyticsWithIndexStatus; - destIndexPatternDeleted: DeleteDataFrameAnalyticsWithIndexStatus; + destDataViewDeleted: DeleteDataFrameAnalyticsWithIndexStatus; } export interface JobsExistsResponse { @@ -83,12 +83,15 @@ export const dataFrameAnalyticsApiProvider = (httpService: HttpService) => ({ }, createDataFrameAnalytics( analyticsId: string, - analyticsConfig: DeepPartial + analyticsConfig: DeepPartial, + createDataView: boolean = false, + timeFieldName?: string ) { const body = JSON.stringify(analyticsConfig); return httpService.http({ path: `${ML_INTERNAL_BASE_PATH}/data_frame/analytics/${analyticsId}`, method: 'PUT', + query: { createDataView, timeFieldName }, body, version: '1', }); @@ -152,11 +155,11 @@ export const dataFrameAnalyticsApiProvider = (httpService: HttpService) => ({ deleteDataFrameAnalyticsAndDestIndex( analyticsId: string, deleteDestIndex: boolean, - deleteDestIndexPattern: boolean + deleteDestDataView: boolean ) { return httpService.http({ path: `${ML_INTERNAL_BASE_PATH}/data_frame/analytics/${analyticsId}`, - query: { deleteDestIndex, deleteDestIndexPattern }, + query: { deleteDestIndex, deleteDestDataView }, method: 'DELETE', version: '1', }); diff --git a/x-pack/plugins/ml/public/application/util/index_utils.ts b/x-pack/plugins/ml/public/application/util/index_utils.ts index faa663e579bc5..5ec52d74c07c4 100644 --- a/x-pack/plugins/ml/public/application/util/index_utils.ts +++ b/x-pack/plugins/ml/public/application/util/index_utils.ts @@ -126,9 +126,9 @@ export function timeBasedIndexCheck(dataView: DataView, showNotification = false } /** - * Returns true if the data view index pattern contains a : + * Returns true if the index pattern contains a : * which means it is cross-cluster */ -export function isCcsIndexPattern(dataViewIndexPattern: string) { - return dataViewIndexPattern.includes(':'); +export function isCcsIndexPattern(indexPattern: string) { + return indexPattern.includes(':'); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx index 7186148caf6e4..a64a9f0bb859e 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx @@ -79,7 +79,7 @@ export const EmbeddableSwimLaneContainer: FC = ( services, chartWidth, fromPage, - { onRenderComplete, onError, onLoading } + { onError, onLoading } ); useEffect(() => { @@ -108,6 +108,7 @@ export const EmbeddableSwimLaneContainer: FC = ( ); if (error) { + onRenderComplete(); return ( = ( /> } chartsService={chartsService} + onRenderComplete={onRenderComplete} />
    ); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts index eace431179dad..4e0f491007cd0 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts @@ -22,7 +22,6 @@ describe('useSwimlaneInputResolver', () => { const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const renderCallbacks = { - onRenderComplete: jest.fn(), onLoading: jest.fn(), onError: jest.fn(), }; @@ -113,7 +112,6 @@ describe('useSwimlaneInputResolver', () => { expect(services[2].anomalyTimelineService.loadOverallData).toHaveBeenCalledTimes(1); expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(1); - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(1); await act(async () => { embeddableInput.next({ @@ -130,7 +128,6 @@ describe('useSwimlaneInputResolver', () => { expect(services[2].anomalyTimelineService.loadOverallData).toHaveBeenCalledTimes(2); expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(2); - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(2); await act(async () => { embeddableInput.next({ @@ -147,7 +144,6 @@ describe('useSwimlaneInputResolver', () => { expect(services[2].anomalyTimelineService.loadOverallData).toHaveBeenCalledTimes(3); expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(3); - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(3); }); test('should not complete the observable on error', async () => { diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts index d543ff4cc9bf1..8669c3b7bd523 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts @@ -50,8 +50,7 @@ export function useSwimlaneInputResolver( services: [CoreStart, MlStartDependencies, AnomalySwimlaneServices], chartWidth: number, fromPage: number, - renderCallbacks: { - onRenderComplete: () => void; + reportingCallbacks: { onLoading: () => void; onError: (error: Error) => void; } @@ -131,7 +130,7 @@ export function useSwimlaneInputResolver( tap(setIsLoading.bind(null, true)), debounceTime(FETCH_RESULTS_DEBOUNCE_MS), tap(() => { - renderCallbacks.onLoading(); + reportingCallbacks.onLoading(); }), switchMap(([explorerJobs, input, bucketInterval, fromPageInput, perPageFromState]) => { if (!explorerJobs) { @@ -246,18 +245,11 @@ export function useSwimlaneInputResolver( useEffect(() => { if (error) { - renderCallbacks.onError(error); + reportingCallbacks.onError(error); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [error]); - useEffect(() => { - if (swimlaneData) { - renderCallbacks.onRenderComplete(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [swimlaneData]); - return [ swimlaneType, swimlaneData, diff --git a/x-pack/plugins/ml/readme.md b/x-pack/plugins/ml/readme.md index 72739cc79fffe..235cc4e0458bf 100644 --- a/x-pack/plugins/ml/readme.md +++ b/x-pack/plugins/ml/readme.md @@ -149,7 +149,7 @@ With PATH_TO_CONFIG and other options as follows. - PATH_TO_CONFIG: `test/accessibility/config.ts` - Add `--grep=ml` to the test runner command - - Tests are located in `x-pack/test/accessibility/apps` + - Tests are located in `x-pack/test/accessibility/apps/group2` ## Generating docs screenshots diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts index cd5e50acdc129..f959683b6ae88 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_manager.ts @@ -67,6 +67,16 @@ export class AnalyticsManager { this._jobs = jobs.data_frame_analytics; } + private getMissingJobNode(label: string): AnalyticsMapNodeElement { + return { + data: { + id: `${label}-${JOB_MAP_NODE_TYPES.ANALYTICS}`, + label, + type: JOB_MAP_NODE_TYPES.ANALYTICS_JOB_MISSING, + }, + }; + } + private isDuplicateElement(analyticsId: string, elements: MapElements[]): boolean { let isDuplicate = false; elements.forEach((elem) => { @@ -106,12 +116,8 @@ export class AnalyticsManager { ); } - private findJob(id: string): estypes.MlDataframeAnalyticsSummary { - const job = this._jobs.find((js) => js.id === id); - if (job === undefined) { - throw Error(`No known job with id '${id}'`); - } - return job; + private findJob(id: string): estypes.MlDataframeAnalyticsSummary | undefined { + return this._jobs.find((js) => js.id === id); } private findTrainedModel(id: string): estypes.MlTrainedModelConfig { @@ -156,14 +162,16 @@ export class AnalyticsManager { private getAnalyticsModelElements( analyticsId: string, - analyticsCreateTime: number + analyticsCreateTime?: number ): { modelElement?: AnalyticsMapNodeElement; modelDetails?: any; edgeElement?: AnalyticsMapEdgeElement; } { // Get trained model for analytics job and create model node - const analyticsModel = this.findJobModel(analyticsId, analyticsCreateTime); + const analyticsModel = analyticsCreateTime + ? this.findJobModel(analyticsId, analyticsCreateTime) + : undefined; let modelElement; let edgeElement; @@ -221,7 +229,7 @@ export class AnalyticsManager { const resultElements = []; const modelElements = []; const details: any = {}; - let data: estypes.MlTrainedModelConfig | estypes.MlDataframeAnalyticsSummary; + let data: estypes.MlTrainedModelConfig | estypes.MlDataframeAnalyticsSummary | undefined; // fetch model data and create model elements data = this.findTrainedModel(modelId); const modelNodeId = `${data.model_id}-${JOB_MAP_NODE_TYPES.TRAINED_MODEL}`; @@ -243,37 +251,35 @@ export class AnalyticsManager { details[modelNodeId] = data; // fetch source job data and create elements if (sourceJobId !== undefined) { - try { - data = this.findJob(sourceJobId); - - nextLinkId = data?.source?.index[0]; - nextType = JOB_MAP_NODE_TYPES.INDEX; - - previousNodeId = `${data.id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; - - resultElements.push({ - data: { - id: previousNodeId, - label: data.id, - type: JOB_MAP_NODE_TYPES.ANALYTICS, - analysisType: getAnalysisType(data?.analysis), - }, - }); - // Create edge between job and model - modelElements.push({ - data: { - id: `${previousNodeId}~${modelNodeId}`, - source: previousNodeId, - target: modelNodeId, - }, - }); + data = this.findJob(sourceJobId); + + nextLinkId = data?.source?.index[0]; + nextType = JOB_MAP_NODE_TYPES.INDEX; + previousNodeId = `${data?.id ?? sourceJobId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; + // If data is undefined - job wasn't found. Create missing job node. + resultElements.push( + data === undefined + ? this.getMissingJobNode(sourceJobId) + : { + data: { + id: previousNodeId, + label: data.id, + type: JOB_MAP_NODE_TYPES.ANALYTICS, + analysisType: getAnalysisType(data?.analysis), + }, + } + ); + // Create edge between job and model + modelElements.push({ + data: { + id: `${previousNodeId}~${modelNodeId}`, + source: previousNodeId, + target: modelNodeId, + }, + }); + if (data) { details[previousNodeId] = data; - } catch (error) { - // fail silently if job doesn't exist - if (error.statusCode !== 404) { - throw error.body ?? error; - } } } @@ -295,21 +301,25 @@ export class AnalyticsManager { const nextLinkId = data?.source?.index[0]; const nextType: JobMapNodeTypes = JOB_MAP_NODE_TYPES.INDEX; + const previousNodeId = `${data?.id ?? jobId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; - const previousNodeId = `${data.id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; - - resultElements.push({ - data: { - id: previousNodeId, - label: data.id, - type: JOB_MAP_NODE_TYPES.ANALYTICS, - analysisType: getAnalysisType(data?.analysis), - isRoot: true, - }, - }); - - details[previousNodeId] = data; + resultElements.push( + data === undefined + ? this.getMissingJobNode(jobId) + : { + data: { + id: previousNodeId, + label: data.id, + type: JOB_MAP_NODE_TYPES.ANALYTICS, + analysisType: getAnalysisType(data?.analysis), + isRoot: true, + }, + } + ); + if (data) { + details[previousNodeId] = data; + } const { modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements( jobId, jobCreateTime @@ -429,33 +439,40 @@ export class AnalyticsManager { nextType = JOB_MAP_NODE_TYPES.TRANSFORM; } } else if (isJobDataLinkReturnType(link) && link.isJob === true) { + // Create missing job node here if job is undefined data = link.jobData; - const nodeId = `${data.id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; + const nodeId = `${data?.id ?? nextLinkId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; previousNodeId = nodeId; - result.elements.unshift({ - data: { - id: nodeId, - label: data.id, - type: JOB_MAP_NODE_TYPES.ANALYTICS, - analysisType: getAnalysisType(data?.analysis), - }, - }); + result.elements.unshift( + data === undefined + ? this.getMissingJobNode(nextLinkId) + : { + data: { + id: nodeId, + label: data.id, + type: JOB_MAP_NODE_TYPES.ANALYTICS, + analysisType: getAnalysisType(data?.analysis), + }, + } + ); result.details[nodeId] = data; nextLinkId = data?.source?.index[0]; nextType = JOB_MAP_NODE_TYPES.INDEX; - // Get trained model for analytics job and create model node - ({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements( - data.id, - data.create_time - )); - if (isAnalyticsMapNodeElement(modelElement)) { - modelElements.push(modelElement); - result.details[modelElement.data.id] = modelDetails; - } - if (isAnalyticsMapEdgeElement(edgeElement)) { - modelElements.push(edgeElement); + if (data) { + // Get trained model for analytics job and create model node + ({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements( + data.id, + data.create_time + )); + if (isAnalyticsMapNodeElement(modelElement)) { + modelElements.push(modelElement); + result.details[modelElement.data.id] = modelDetails; + } + if (isAnalyticsMapEdgeElement(edgeElement)) { + modelElements.push(edgeElement); + } } } else if (isTransformLinkReturnType(link) && link.isTransform === true) { data = link.transformData; @@ -626,7 +643,7 @@ export class AnalyticsManager { if (analyticsId !== undefined) { const jobData = this.findJob(analyticsId); - const currentJobNodeId = `${jobData.id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; + const currentJobNodeId = `${jobData?.id ?? analyticsId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`; rootIndex = Array.isArray(jobData?.dest?.index) ? jobData?.dest?.index[0] : jobData?.dest?.index; @@ -635,7 +652,7 @@ export class AnalyticsManager { // Fetch trained model for incoming job id and add node and edge const { modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements( analyticsId, - jobData.create_time! + jobData?.create_time ); if (isAnalyticsMapNodeElement(modelElement)) { result.elements.push(modelElement); diff --git a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts index 679cfd49ef637..5267a95d4fb48 100644 --- a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts +++ b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts @@ -56,6 +56,7 @@ describe('modelsProvider', () => { hidden: true, name: '.elser_model_1', version: 1, + modelName: 'elser', }, { config: { input: { field_names: ['text_field'] } }, @@ -63,6 +64,7 @@ describe('modelsProvider', () => { description: 'Elastic Learned Sparse EncodeR v2', name: '.elser_model_2', version: 2, + modelName: 'elser', }, { arch: 'amd64', @@ -72,6 +74,7 @@ describe('modelsProvider', () => { os: 'Linux', recommended: true, version: 2, + modelName: 'elser', }, ]); }); @@ -107,6 +110,7 @@ describe('modelsProvider', () => { hidden: true, name: '.elser_model_1', version: 1, + modelName: 'elser', }, { config: { input: { field_names: ['text_field'] } }, @@ -114,6 +118,7 @@ describe('modelsProvider', () => { description: 'Elastic Learned Sparse EncodeR v2', name: '.elser_model_2', version: 2, + modelName: 'elser', }, { arch: 'amd64', @@ -122,6 +127,7 @@ describe('modelsProvider', () => { name: '.elser_model_2_linux-x86_64', os: 'Linux', version: 2, + modelName: 'elser', }, ]); }); diff --git a/x-pack/plugins/ml/server/models/model_management/models_provider.ts b/x-pack/plugins/ml/server/models/model_management/models_provider.ts index db8b0b0d6503e..e6243b38324bf 100644 --- a/x-pack/plugins/ml/server/models/model_management/models_provider.ts +++ b/x-pack/plugins/ml/server/models/model_management/models_provider.ts @@ -465,10 +465,10 @@ export class ModelsProvider { (isCloud && def.os === 'Linux' && def.arch === 'amd64') || (sameArch && !!def?.os && def?.os === osName && def?.arch === arch); - const { modelName, ...rest } = def; + const { modelName } = def; const modelDefinitionResponse = { - ...rest, + ...def, ...(recommended ? { recommended } : {}), name, }; diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index 302a8b2c89bbf..b951c23ed4d86 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -6,49 +6,43 @@ */ import type { IScopedClusterClient } from '@kbn/core/server'; -import type { DataViewsService } from '@kbn/data-views-plugin/common'; +import type { RuntimeField } from '@kbn/data-views-plugin/common'; import type { Field, Aggregation } from '@kbn/ml-anomaly-utils'; import { JOB_MAP_NODE_TYPES, type DeleteDataFrameAnalyticsWithIndexStatus, } from '@kbn/ml-data-frame-analytics-utils'; import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import { dataViewCreateQuerySchema } from '@kbn/ml-data-view-utils/schemas/api_create_query_schema'; +import { createDataViewFn } from '@kbn/ml-data-view-utils/actions/create'; +import { deleteDataViewFn } from '@kbn/ml-data-view-utils/actions/delete'; + import { type MlFeatures, ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; import { wrapError } from '../client/error_wrapper'; import { analyticsAuditMessagesProvider } from '../models/data_frame_analytics/analytics_audit_messages'; import type { RouteInitialization } from '../types'; import { - dataAnalyticsJobConfigSchema, - dataAnalyticsJobUpdateSchema, - dataAnalyticsEvaluateSchema, - dataAnalyticsExplainSchema, - analyticsIdSchema, - analyticsMapQuerySchema, + dataFrameAnalyticsJobConfigSchema, + dataFrameAnalyticsJobUpdateSchema, + dataFrameAnalyticsEvaluateSchema, + dataFrameAnalyticsExplainSchema, + dataFrameAnalyticsIdSchema, + dataFrameAnalyticsMapQuerySchema, stopsDataFrameAnalyticsJobQuerySchema, deleteDataFrameAnalyticsJobSchema, - jobsExistSchema, - analyticsQuerySchema, - analyticsNewJobCapsParamsSchema, - analyticsNewJobCapsQuerySchema, -} from './schemas/data_analytics_schema'; + dataFrameAnalyticsJobsExistSchema, + dataFrameAnalyticsQuerySchema, + dataFrameAnalyticsNewJobCapsParamsSchema, + dataFrameAnalyticsNewJobCapsQuerySchema, + type PutDataFrameAnalyticsResponseSchema, +} from './schemas/data_frame_analytics_schema'; import type { ExtendAnalyticsMapArgs } from '../models/data_frame_analytics/types'; -import { DataViewHandler } from '../models/data_frame_analytics/index_patterns'; import { AnalyticsManager } from '../models/data_frame_analytics/analytics_manager'; import { validateAnalyticsJob } from '../models/data_frame_analytics/validation'; import { fieldServiceProvider } from '../models/job_service/new_job_caps/field_service'; import { getAuthorizationHeader } from '../lib/request_authorization'; import type { MlClient } from '../lib/ml_client'; -function getDataViewId(dataViewsService: DataViewsService, patternName: string) { - const iph = new DataViewHandler(dataViewsService); - return iph.getDataViewId(patternName); -} - -function deleteDestDataViewById(dataViewsService: DataViewsService, dataViewId: string) { - const iph = new DataViewHandler(dataViewsService); - return iph.deleteDataViewById(dataViewId); -} - function getExtendedMap( mlClient: MlClient, client: IScopedClusterClient, @@ -144,7 +138,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - query: analyticsQuerySchema, + query: dataFrameAnalyticsQuerySchema, }, }, }, @@ -185,8 +179,8 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, - query: analyticsQuerySchema, + params: dataFrameAnalyticsIdSchema, + query: dataFrameAnalyticsQuerySchema, }, }, }, @@ -262,7 +256,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, + params: dataFrameAnalyticsIdSchema, }, }, }, @@ -305,29 +299,65 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, - body: dataAnalyticsJobConfigSchema, + params: dataFrameAnalyticsIdSchema, + query: dataViewCreateQuerySchema, + body: dataFrameAnalyticsJobConfigSchema, }, }, }, - routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { - try { + routeGuard.fullLicenseAPIGuard( + async ({ mlClient, request, response, getDataViewsService }) => { const { analyticsId } = request.params; - const body = await mlClient.putDataFrameAnalytics( - { + const { createDataView, timeFieldName } = request.query; + + const fullResponse: PutDataFrameAnalyticsResponseSchema = { + dataFrameAnalyticsJobsCreated: [], + dataFrameAnalyticsJobsErrors: [], + dataViewsCreated: [], + dataViewsErrors: [], + }; + + try { + const resp = await mlClient.putDataFrameAnalytics( + { + id: analyticsId, + // @ts-expect-error @elastic-elasticsearch Data frame types incomplete + body: request.body, + }, + getAuthorizationHeader(request) + ); + + if (resp.id && resp.create_time) { + fullResponse.dataFrameAnalyticsJobsCreated.push({ id: analyticsId }); + } else { + fullResponse.dataFrameAnalyticsJobsErrors.push({ + id: analyticsId, + error: wrapError(resp), + }); + } + } catch (e) { + fullResponse.dataFrameAnalyticsJobsErrors.push({ id: analyticsId, - // @ts-expect-error @elastic-elasticsearch Data frame types incomplete - body: request.body, - }, - getAuthorizationHeader(request) - ); - return response.ok({ - body, - }); - } catch (e) { - return response.customError(wrapError(e)); + error: wrapError(e), + }); + } + + if (createDataView) { + const { dataViewsCreated, dataViewsErrors } = await createDataViewFn({ + dataViewsService: await getDataViewsService(), + dataViewName: request.body.dest.index, + runtimeMappings: request.body.source.runtime_mappings as Record, + timeFieldName, + errorFallbackId: analyticsId, + }); + + fullResponse.dataViewsCreated = dataViewsCreated; + fullResponse.dataViewsErrors = dataViewsErrors; + } + + return response.ok({ body: fullResponse }); } - }) + ) ); /** @@ -352,7 +382,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - body: dataAnalyticsEvaluateSchema, + body: dataFrameAnalyticsEvaluateSchema, }, }, }, @@ -397,7 +427,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - body: dataAnalyticsExplainSchema, + body: dataFrameAnalyticsExplainSchema, }, }, }, @@ -440,7 +470,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, + params: dataFrameAnalyticsIdSchema, query: deleteDataFrameAnalyticsJobSchema, }, }, @@ -449,11 +479,11 @@ export function dataFrameAnalyticsRoutes( async ({ mlClient, client, request, response, getDataViewsService }) => { try { const { analyticsId } = request.params; - const { deleteDestIndex, deleteDestIndexPattern } = request.query; + const { deleteDestIndex, deleteDestDataView } = request.query; let destinationIndex: string | undefined; const analyticsJobDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { success: false }; const destIndexDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { success: false }; - const destIndexPatternDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { + let destDataViewDeleted: DeleteDataFrameAnalyticsWithIndexStatus = { success: false, }; @@ -473,7 +503,7 @@ export function dataFrameAnalyticsRoutes( return response.customError(wrapError(e)); } - if (deleteDestIndex || deleteDestIndexPattern) { + if (deleteDestIndex || deleteDestDataView) { // If user checks box to delete the destinationIndex associated with the job if (destinationIndex && deleteDestIndex) { // Verify if user has privilege to delete the destination index @@ -493,18 +523,12 @@ export function dataFrameAnalyticsRoutes( } } - // Delete the index pattern if there's an index pattern that matches the name of dest index - if (destinationIndex && deleteDestIndexPattern) { - try { - const dataViewsService = await getDataViewsService(); - const dataViewId = await getDataViewId(dataViewsService, destinationIndex); - if (dataViewId) { - await deleteDestDataViewById(dataViewsService, dataViewId); - } - destIndexPatternDeleted.success = true; - } catch (deleteDestIndexPatternError) { - destIndexPatternDeleted.error = deleteDestIndexPatternError; - } + // Delete the data view if there's a data view that matches the name of dest index + if (destinationIndex && deleteDestDataView) { + destDataViewDeleted = await deleteDataViewFn({ + dataViewsService: await getDataViewsService(), + dataViewName: destinationIndex, + }); } } // Grab the target index from the data frame analytics job id @@ -521,7 +545,7 @@ export function dataFrameAnalyticsRoutes( const results = { analyticsJobDeleted, destIndexDeleted, - destIndexPatternDeleted, + destDataViewDeleted, }; return response.ok({ body: results, @@ -555,7 +579,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, + params: dataFrameAnalyticsIdSchema, }, }, }, @@ -597,7 +621,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, + params: dataFrameAnalyticsIdSchema, query: stopsDataFrameAnalyticsJobQuerySchema, }, }, @@ -640,8 +664,8 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, - body: dataAnalyticsJobUpdateSchema, + params: dataFrameAnalyticsIdSchema, + body: dataFrameAnalyticsJobUpdateSchema, }, }, }, @@ -686,7 +710,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, + params: dataFrameAnalyticsIdSchema, }, }, }, @@ -728,7 +752,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - body: jobsExistSchema, + body: dataFrameAnalyticsJobsExistSchema, }, }, }, @@ -785,8 +809,8 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsIdSchema, - query: analyticsMapQuerySchema, + params: dataFrameAnalyticsIdSchema, + query: dataFrameAnalyticsMapQuerySchema, }, }, }, @@ -851,8 +875,8 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - params: analyticsNewJobCapsParamsSchema, - query: analyticsNewJobCapsQuerySchema, + params: dataFrameAnalyticsNewJobCapsParamsSchema, + query: dataFrameAnalyticsNewJobCapsQuerySchema, }, }, }, @@ -906,7 +930,7 @@ export function dataFrameAnalyticsRoutes( version: '1', validate: { request: { - body: dataAnalyticsJobConfigSchema, + body: dataFrameAnalyticsJobConfigSchema, }, }, }, diff --git a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts b/x-pack/plugins/ml/server/routes/schemas/data_frame_analytics_schema.ts similarity index 72% rename from x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts rename to x-pack/plugins/ml/server/routes/schemas/data_frame_analytics_schema.ts index 7b67a56fe485e..050722816eeb4 100644 --- a/x-pack/plugins/ml/server/routes/schemas/data_analytics_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/data_frame_analytics_schema.ts @@ -6,9 +6,11 @@ */ import { schema } from '@kbn/config-schema'; +import type { CreateDataViewApiResponseSchema } from '@kbn/ml-data-view-utils/types/api_create_response_schema'; + import { runtimeMappingsSchema } from './runtime_mappings_schema'; -export const dataAnalyticsJobConfigSchema = schema.object({ +export const dataFrameAnalyticsJobConfigSchema = schema.object({ description: schema.maybe(schema.string()), _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), dest: schema.object({ @@ -35,7 +37,7 @@ export const dataAnalyticsJobConfigSchema = schema.object({ max_num_threads: schema.maybe(schema.number()), }); -export const dataAnalyticsEvaluateSchema = schema.object({ +export const dataFrameAnalyticsEvaluateSchema = schema.object({ index: schema.string(), query: schema.maybe(schema.any()), evaluation: schema.maybe( @@ -47,7 +49,7 @@ export const dataAnalyticsEvaluateSchema = schema.object({ ), }); -export const dataAnalyticsExplainSchema = schema.object({ +export const dataFrameAnalyticsExplainSchema = schema.object({ description: schema.maybe(schema.string()), dest: schema.maybe(schema.any()), /** Source */ @@ -63,14 +65,14 @@ export const dataAnalyticsExplainSchema = schema.object({ _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); -export const analyticsIdSchema = schema.object({ +export const dataFrameAnalyticsIdSchema = schema.object({ /** * Analytics ID */ analyticsId: schema.string(), }); -export const analyticsQuerySchema = schema.object({ +export const dataFrameAnalyticsQuerySchema = schema.object({ /** * Analytics Query */ @@ -83,10 +85,10 @@ export const deleteDataFrameAnalyticsJobSchema = schema.object({ * Analytics Destination Index */ deleteDestIndex: schema.maybe(schema.boolean()), - deleteDestIndexPattern: schema.maybe(schema.boolean()), + deleteDestDataView: schema.maybe(schema.boolean()), }); -export const dataAnalyticsJobUpdateSchema = schema.object({ +export const dataFrameAnalyticsJobUpdateSchema = schema.object({ description: schema.maybe(schema.string()), model_memory_limit: schema.maybe(schema.string()), allow_lazy_start: schema.maybe(schema.boolean()), @@ -98,17 +100,32 @@ export const stopsDataFrameAnalyticsJobQuerySchema = schema.object({ force: schema.maybe(schema.boolean()), }); -export const jobsExistSchema = schema.object({ +export const dataFrameAnalyticsJobsExistSchema = schema.object({ analyticsIds: schema.arrayOf(schema.string()), allSpaces: schema.maybe(schema.boolean()), }); -export const analyticsMapQuerySchema = schema.maybe( +export const dataFrameAnalyticsMapQuerySchema = schema.maybe( schema.object({ treatAsRoot: schema.maybe(schema.any()), type: schema.maybe(schema.string()) }) ); -export const analyticsNewJobCapsParamsSchema = schema.object({ indexPattern: schema.string() }); +export const dataFrameAnalyticsNewJobCapsParamsSchema = schema.object({ + indexPattern: schema.string(), +}); -export const analyticsNewJobCapsQuerySchema = schema.maybe( +export const dataFrameAnalyticsNewJobCapsQuerySchema = schema.maybe( schema.object({ rollup: schema.maybe(schema.string()) }) ); + +interface DataFrameAnalyticsJobsCreated { + id: string; +} +interface CreatedError { + id: string; + error: any; +} + +export interface PutDataFrameAnalyticsResponseSchema extends CreateDataViewApiResponseSchema { + dataFrameAnalyticsJobsCreated: DataFrameAnalyticsJobsCreated[]; + dataFrameAnalyticsJobsErrors: CreatedError[]; +} diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 846825569da66..f1383ef078c89 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -111,5 +111,7 @@ "@kbn/alerts-as-data-utils", "@kbn/rule-registry-plugin", "@kbn/securitysolution-ecs", + "@kbn/ml-data-view-utils", + "@kbn/ml-creation-wizard-utils", ], } diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 64dcf3489a7f9..0d6a6b169fc88 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -67,7 +67,7 @@ describe('config schema', () => { "logFetchCount": 10, "logQueries": false, "maxIdleSockets": 256, - "maxSockets": Infinity, + "maxSockets": 800, "pingTimeout": "PT30S", "requestHeadersWhitelist": Array [ "authorization", diff --git a/x-pack/plugins/notifications/README.md b/x-pack/plugins/notifications/README.mdx similarity index 100% rename from x-pack/plugins/notifications/README.md rename to x-pack/plugins/notifications/README.mdx diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/types.ts b/x-pack/plugins/observability/common/custom_threshold_rule/types.ts index 40d569152d1a3..7668e19dc3900 100644 --- a/x-pack/plugins/observability/common/custom_threshold_rule/types.ts +++ b/x-pack/plugins/observability/common/custom_threshold_rule/types.ts @@ -8,7 +8,6 @@ import * as rt from 'io-ts'; import { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { TimeUnitChar } from '../utils/formatters/duration'; -import { CUSTOM_AGGREGATOR } from './constants'; export const ThresholdFormatterTypeRT = rt.keyof({ abbreviatedNumber: null, @@ -84,7 +83,6 @@ export interface CustomThresholdExpressionMetric { } export interface CustomMetricExpressionParams extends BaseMetricExpressionParams { - aggType: typeof CUSTOM_AGGREGATOR; metrics: CustomThresholdExpressionMetric[]; equation?: string; label?: string; diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.json b/x-pack/plugins/observability/docs/openapi/slo/bundled.json index b4f52b032a9fc..ff366afc2ff1f 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.json @@ -753,7 +753,8 @@ "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "ApiKey" + "name": "Authorization", + "description": "e.g. Authorization: ApiKey base64AccessApiKey" } }, "parameters": { diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml index 8efdbd9dfe2c2..5aa20726b6a07 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml @@ -455,7 +455,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' parameters: kbn_xsrf: schema: diff --git a/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml index 687fd94f006a4..910f795aa40a7 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml @@ -35,7 +35,8 @@ components: apiKeyAuth: type: apiKey in: header - name: ApiKey + name: Authorization + description: 'e.g. Authorization: ApiKey base64AccessApiKey' security: - basicAuth: [] - apiKeyAuth: [] diff --git a/x-pack/plugins/observability/kibana.jsonc b/x-pack/plugins/observability/kibana.jsonc index c03e0b499d424..083f1956ec31f 100644 --- a/x-pack/plugins/observability/kibana.jsonc +++ b/x-pack/plugins/observability/kibana.jsonc @@ -34,12 +34,12 @@ "unifiedSearch", "visualizations", "dashboard", - "expressions" + "expressions", + "licensing" ], "optionalPlugins": [ "discover", "home", - "licensing", "usageCollection", "cloud", "spaces", diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 8eb2bfe7df7bb..4a99d3648af34 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -20,7 +20,6 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; -import { HasDataContextProvider } from '../context/has_data_context/has_data_context'; import { PluginContext } from '../context/plugin_context/plugin_context'; import { ConfigSchema, ObservabilityPublicPluginsStart } from '../plugin'; import { routes } from '../routes/routes'; @@ -119,9 +118,7 @@ export const renderApp = ({ data-test-subj="observabilityMainContainer" > - - - + diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability/public/components/custom_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap index 2e12193435726..0446f2fe1d4a2 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap @@ -22,7 +22,6 @@ Array [ "title": "unknown-index", }, "expression": Object { - "aggType": "custom", "comparator": ">", "metrics": Array [ Object { diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx index a5a029d764a89..deebe5a6cd4ca 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx @@ -134,7 +134,7 @@ export default function AlertDetailsAppSection({ -

    {criterion.aggType.toUpperCase()}

    +

    {criterion.label || 'CUSTOM'}

    = { expression: { - aggType: CUSTOM_AGGREGATOR, metrics: [ { name: 'A', @@ -120,7 +118,6 @@ CustomEquationEditorDefault.args = { CustomEquationEditorWithEquationErrors.args = { ...BASE_ARGS, expression: { - aggType: CUSTOM_AGGREGATOR, equation: 'Math.round(A / B)', metrics: [ { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.cpu.user.pct' }, diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx index 164db3bac1682..e15248d72c497 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx @@ -22,6 +22,7 @@ import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataViewBase } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; +import { adjustThresholdBasedOnFormat } from '../../helpers/adjust_threshold_based_on_format'; import { Aggregators, CustomThresholdExpressionMetric, @@ -71,7 +72,12 @@ export function CustomEquationEditor({ const currentVars = previous?.map((m) => m.name) ?? []; const name = first(xor(VAR_NAMES, currentVars))!; const nextMetrics = [...(previous || []), { ...NEW_METRIC, name }]; - debouncedOnChange({ ...expression, metrics: nextMetrics, equation }); + debouncedOnChange({ + ...expression, + metrics: nextMetrics, + equation, + threshold: adjustThresholdBasedOnFormat(previous, nextMetrics, expression.threshold), + }); return nextMetrics; }); }, [debouncedOnChange, equation, expression]); @@ -81,7 +87,12 @@ export function CustomEquationEditor({ setCustomMetrics((previous) => { const nextMetrics = previous?.filter((row) => row.name !== name) ?? [NEW_METRIC]; const finalMetrics = (nextMetrics.length && nextMetrics) || [NEW_METRIC]; - debouncedOnChange({ ...expression, metrics: finalMetrics, equation }); + debouncedOnChange({ + ...expression, + metrics: finalMetrics, + equation, + threshold: adjustThresholdBasedOnFormat(previous, nextMetrics, expression.threshold), + }); return finalMetrics; }); }, @@ -92,7 +103,12 @@ export function CustomEquationEditor({ (metric: CustomThresholdExpressionMetric) => { setCustomMetrics((previous) => { const nextMetrics = previous?.map((m) => (m.name === metric.name ? metric : m)); - debouncedOnChange({ ...expression, metrics: nextMetrics, equation }); + debouncedOnChange({ + ...expression, + metrics: nextMetrics, + equation, + threshold: adjustThresholdBasedOnFormat(previous, nextMetrics, expression.threshold), + }); return nextMetrics; }); }, diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_controls.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_controls.tsx index d43c09d7c3787..305be75390df8 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_controls.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_controls.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiFlexItem, EuiButtonIcon } from '@elastic/eui'; +import { EuiButtonIcon } from '@elastic/eui'; import { DELETE_LABEL } from '../../i18n_strings'; interface MetricRowControlProps { @@ -15,19 +15,16 @@ interface MetricRowControlProps { export function MetricRowControls({ onDelete, disableDelete }: MetricRowControlProps) { return ( - <> - - - - + ); } diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_with_agg.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_with_agg.tsx index 51c4713fef264..a00a086ef5528 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_with_agg.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/metric_row_with_agg.tsx @@ -112,117 +112,123 @@ export function MetricRowWithAgg({ const isFieldInvalid = get(errors, ['metrics', name, 'field']) != null || !field; return ( - <> - - - - { - setAggTypePopoverOpen(true); - }} - isInvalid={aggType !== Aggregators.COUNT && !field} - /> - - } - isOpen={aggTypePopoverOpen} - closePopover={() => { - setAggTypePopoverOpen(false); - }} - display="block" - ownFocus - anchorPosition={'downLeft'} - repositionOnScroll - > -
    - setAggTypePopoverOpen(false)}> - - + + + + + {i18n.translate( + 'xpack.observability.customThreshold.rule.alertFlyout.customEquationEditor.aggregationLabel', + { defaultMessage: 'Aggregation {name}', values: { name } } + )} + + {!disableDelete && ( + + + + )} + + } + > + { + setAggTypePopoverOpen(true); + }} + isInvalid={aggType !== Aggregators.COUNT && !field} + /> + + } + isOpen={aggTypePopoverOpen} + closePopover={() => { + setAggTypePopoverOpen(false); + }} + display="block" + ownFocus + anchorPosition={'downLeft'} + repositionOnScroll + > +
    + setAggTypePopoverOpen(false)}> + + - - + + + + { + handleAggChange(e.target.value); + }} + options={Object.values(aggregationTypes).map(({ text, value }) => { + return { + text, + value, + }; + })} + isInvalid={isAggInvalid} + /> + + + + {aggType === Aggregators.COUNT ? ( + + + + ) : ( - { - handleAggChange(e.target.value); - }} - options={Object.values(aggregationTypes).map(({ text, value }) => { - return { - text, - value, - }; - })} - isInvalid={isAggInvalid} + isInvalid={isFieldInvalid} + singleSelection={{ asPlainText: true }} + options={fieldOptions} + selectedOptions={field ? [{ label: field }] : []} + onChange={handleFieldChange} /> - - - {aggType === Aggregators.COUNT ? ( - - - - ) : ( - - - - )} - - -
    - - - - - + )} + + +
    +
    +
    +
    ); } diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.test.tsx index 956391a3f29b7..5af6cae385d73 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.test.tsx @@ -12,7 +12,6 @@ import { DataViewBase } from '@kbn/es-query'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import React, { ReactElement } from 'react'; import { act } from 'react-dom/test-utils'; -import { CUSTOM_AGGREGATOR } from '../../../../common/custom_threshold_rule/constants'; import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { ExpressionChart } from './expression_chart'; @@ -77,7 +76,6 @@ describe('ExpressionChart', () => { it('should display no data message', async () => { const expression: MetricExpression = { - aggType: CUSTOM_AGGREGATOR, metrics: [ { name: 'A', diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.test.tsx index 8b0a4de92bd7e..2bdd1f2def081 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.test.tsx @@ -9,7 +9,6 @@ import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { CUSTOM_AGGREGATOR } from '../../../../common/custom_threshold_rule/constants'; import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { ExpressionRow } from './expression_row'; @@ -18,6 +17,7 @@ describe('ExpressionRow', () => { async function setup(expression: MetricExpression) { const wrapper = mountWithIntl( Condition} canDelete={false} fields={[ { @@ -57,7 +57,6 @@ describe('ExpressionRow', () => { it('should display thresholds as a percentage for pct metrics', async () => { const expression: MetricExpression = { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -83,7 +82,6 @@ describe('ExpressionRow', () => { it('should display thresholds as a decimal for all other metrics', async () => { const expression = { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx index 0f53594bd49e9..6b0643791596a 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_row.tsx @@ -12,9 +12,10 @@ import { EuiFlexItem, EuiFormRow, EuiSpacer, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState, ReactElement } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { AggregationType, @@ -47,6 +48,7 @@ const customComparators = { }; interface ExpressionRowProps { + title: ReactElement; fields: DataViewFieldBase[]; expressionId: number; expression: MetricExpression; @@ -77,6 +79,7 @@ export const ExpressionRow: React.FC = (props) => { remove, fields, canDelete, + title, } = props; const { metrics, comparator = Comparator.GT, threshold = [] } = expression; @@ -146,6 +149,29 @@ export const ExpressionRow: React.FC = (props) => { ); return ( <> + + + +
    {title}
    +
    +
    + {canDelete && ( + + remove(expressionId)} + /> + + )} +
    @@ -178,25 +204,8 @@ export const ExpressionRow: React.FC = (props) => { - {canDelete && ( - - remove(expressionId)} - /> - - )} {children} - ); }; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx index 9226da0ca2370..bc478e54135ba 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx @@ -9,12 +9,11 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { DataView } from '@kbn/data-views-plugin/common'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; -import { CUSTOM_AGGREGATOR } from '../../../../../common/custom_threshold_rule/constants'; import { Comparator, Aggregators } from '../../../../../common/custom_threshold_rule/types'; import { useKibana } from '../../../../utils/kibana_react'; import { kibanaStartMock } from '../../../../utils/kibana_react.mock'; import { MetricExpression } from '../../types'; -import { PreviewChart } from './preview_chart'; +import { getBufferThreshold, PreviewChart } from './preview_chart'; jest.mock('../../../../utils/kibana_react'); @@ -55,7 +54,6 @@ describe('Preview chart', () => { it('should display no data message', async () => { const expression: MetricExpression = { - aggType: CUSTOM_AGGREGATOR, metrics: [ { name: 'A', @@ -72,3 +70,18 @@ describe('Preview chart', () => { expect(wrapper.find('[data-test-subj="thresholdRuleNoChartData"]').exists()).toBeTruthy(); }); }); + +describe('getBufferThreshold', () => { + const testData = [ + { threshold: undefined, buffer: '0.00' }, + { threshold: 0.1, buffer: '0.12' }, + { threshold: 0.01, buffer: '0.02' }, + { threshold: 0.001, buffer: '0.01' }, + { threshold: 0.00098, buffer: '0.01' }, + { threshold: 130, buffer: '143.00' }, + ]; + + it.each(testData)('getBufferThreshold($threshold) = $buffer', ({ threshold, buffer }) => { + expect(getBufferThreshold(threshold)).toBe(buffer); + }); +}); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.tsx index d5a8b763ca7b5..1c25d22b3a595 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.tsx @@ -44,6 +44,9 @@ const getOperationTypeFromRuleAggType = (aggType: AggType): OperationType => { return aggType; }; +export const getBufferThreshold = (threshold?: number): string => + (Math.ceil((threshold || 0) * 1.1 * 100) / 100).toFixed(2).toString(); + export function PreviewChart({ metricExpression, dataView, @@ -147,7 +150,7 @@ export function PreviewChart({ const bufferRefLine = new XYReferenceLinesLayer({ data: [ { - value: Math.round((threshold[0] || 0) * 1.1).toString(), + value: getBufferThreshold(threshold[0]), color: 'transparent', fill, format, @@ -218,12 +221,7 @@ export function PreviewChart({ decimals: isPercent ? 0 : 2, }, }, - filter: { - language: 'kuery', - query: filterQuery || '', - }, }; - const xYDataLayerOptions: XYLayerOptions = { buckets: { type: 'date_histogram', @@ -252,7 +250,6 @@ export function PreviewChart({ value: layer.value, label: layer.label, format: layer.format, - filter: layer.filter, })), options: xYDataLayerOptions, }); @@ -333,6 +330,10 @@ export function PreviewChart({ timeRange={{ from: `now-${timeSize * 20}${timeUnit}`, to: 'now' }} attributes={attributes} disableTriggers={true} + query={{ + language: 'kuery', + query: filterQuery || '', + }} />
    ); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx index f360b415fa1be..2144757216fbe 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/validation.tsx @@ -27,17 +27,12 @@ export function validateCustomThreshold({ const validationResult = { errors: {} }; const errors: { [id: string]: { - aggField: string[]; timeSizeUnit: string[]; timeWindowSize: string[]; critical: { threshold0: string[]; threshold1: string[]; }; - warning: { - threshold0: string[]; - threshold1: string[]; - }; metricsError?: string; metrics: Record; equation?: string; @@ -91,23 +86,9 @@ export function validateCustomThreshold({ threshold0: [], threshold1: [], }, - warning: { - threshold0: [], - threshold1: [], - }, metric: [], metrics: {}, }; - if (!c.aggType) { - errors[id].aggField.push( - i18n.translate( - 'xpack.observability.customThreshold.rule.alertFlyout.error.aggregationRequired', - { - defaultMessage: 'Aggregation is required.', - } - ) - ); - } if (!c.threshold || !c.threshold.length) { errors[id].critical.threshold0.push( @@ -120,8 +101,30 @@ export function validateCustomThreshold({ ); } - if (c.warningThreshold && !c.warningThreshold.length) { - errors[id].warning.threshold0.push( + // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. + // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. + const { comparator, threshold } = { comparator: c.comparator, threshold: c.threshold } as { + comparator?: Comparator; + threshold?: number[]; + }; + if (threshold && threshold.length && ![...threshold].every(isNumber)) { + [...threshold].forEach((v, i) => { + if (!isNumber(v)) { + const key = i === 0 ? 'threshold0' : 'threshold1'; + errors[id].critical[key].push( + i18n.translate( + 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired', + { + defaultMessage: 'Thresholds must contain a valid number.', + } + ) + ); + } + }); + } + + if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + errors[id].critical.threshold1.push( i18n.translate( 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdRequired', { @@ -131,45 +134,6 @@ export function validateCustomThreshold({ ); } - for (const props of [ - { comparator: c.comparator, threshold: c.threshold, type: 'critical' }, - { comparator: c.warningComparator, threshold: c.warningThreshold, type: 'warning' }, - ]) { - // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. - // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. - const { comparator, threshold, type } = props as { - comparator?: Comparator; - threshold?: number[]; - type: 'critical' | 'warning'; - }; - if (threshold && threshold.length && ![...threshold].every(isNumber)) { - [...threshold].forEach((v, i) => { - if (!isNumber(v)) { - const key = i === 0 ? 'threshold0' : 'threshold1'; - errors[id][type][key].push( - i18n.translate( - 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired', - { - defaultMessage: 'Thresholds must contain a valid number.', - } - ) - ); - } - }); - } - - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { - errors[id][type].threshold1.push( - i18n.translate( - 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdRequired', - { - defaultMessage: 'Threshold is required.', - } - ) - ); - } - } - if (!c.timeSize) { errors[id].timeWindowSize.push( i18n.translate('xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred', { diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index 78fa6c3244427..0f1742469c312 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -92,10 +92,9 @@ describe('Expression', () => { }, ], comparator: Comparator.GT, - threshold: [1000], + threshold: [100], timeSize: 1, timeUnit: 'm', - aggType: 'custom', }, ]); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 2c5f7438b048f..539f8fbf4f58d 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -14,6 +14,7 @@ import { EuiEmptyPrompt, EuiFormErrorText, EuiFormRow, + EuiHorizontalRule, EuiIcon, EuiLink, EuiLoadingSpinner, @@ -36,7 +37,6 @@ import { } from '@kbn/triggers-actions-ui-plugin/public'; import { useKibana } from '../../utils/kibana_react'; -import { CUSTOM_AGGREGATOR } from '../../../common/custom_threshold_rule/constants'; import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; import { TimeUnitChar } from '../../../common/utils/formatters/duration'; import { AlertContextMeta, AlertParams, MetricExpression } from './types'; @@ -52,7 +52,6 @@ type Props = Omit< >; export const defaultExpression: MetricExpression = { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -60,7 +59,7 @@ export const defaultExpression: MetricExpression = { aggType: Aggregators.COUNT, }, ], - threshold: [1000], + threshold: [100], timeSize: 1, timeUnit: 'm', }; @@ -202,11 +201,8 @@ export default function Expressions(props: Props) { const removeExpression = useCallback( (id: number) => { - const ruleCriteria = ruleParams.criteria?.slice() || []; - if (ruleCriteria.length > 1) { - ruleCriteria.splice(id, 1); - setRuleParams('criteria', ruleCriteria); - } + const ruleCriteria = ruleParams.criteria?.filter((_, index) => index !== id) || []; + setRuleParams('criteria', ruleCriteria); }, [setRuleParams, ruleParams.criteria] ); @@ -375,30 +371,11 @@ export default function Expressions(props: Props) { )} - -
    - -
    -
    {ruleParams.criteria && ruleParams.criteria.map((e, idx) => { return (
    - {/* index has semantic meaning, we show the condition title starting from the 2nd one */} - {idx >= 1 && ( - -
    - -
    -
    - )} + {idx > 0 && } 1) || false} fields={derivedIndexPattern.fields} @@ -410,6 +387,20 @@ export default function Expressions(props: Props) { errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} dataView={derivedIndexPattern} + title={ + ruleParams.criteria.length === 1 ? ( + + ) : ( + + ) + } > { + test('previous: nonPercent, next: percent -> threshold / 100', () => { + const previous: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + aggType: Aggregators.COUNT, + }, + ]; + const next: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + field: 'system.cpu.system.pct', + aggType: Aggregators.AVERAGE, + }, + ]; + const threshold: number[] = [100]; + const expectedThreshold: number[] = [1]; + + expect(adjustThresholdBasedOnFormat(previous, next, threshold)).toEqual(expectedThreshold); + }); + + test('previous: percent, next: nonPercent -> threshold * 100', () => { + const previous: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + field: 'system.cpu.system.pct', + aggType: Aggregators.AVERAGE, + }, + ]; + const next: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + aggType: Aggregators.COUNT, + }, + ]; + const threshold: number[] = [1]; + const expectedThreshold: number[] = [100]; + + expect(adjustThresholdBasedOnFormat(previous, next, threshold)).toEqual(expectedThreshold); + }); + + test('previous: percent, next: percent -> no threshold change', () => { + const previous: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + field: 'system.cpu.system.pct', + aggType: Aggregators.AVERAGE, + }, + ]; + const next: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + field: 'system.cpu.total.norm.pct', + aggType: Aggregators.AVERAGE, + }, + ]; + const threshold: number[] = [1]; + + expect(adjustThresholdBasedOnFormat(previous, next, threshold)).toEqual(threshold); + }); + + test('previous: nonPercent, next: nonPercent -> threshold * 100', () => { + const previous: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + field: 'host.disk.read.bytes', + aggType: Aggregators.AVERAGE, + }, + ]; + const next: CustomThresholdExpressionMetric[] = [ + { + name: 'A', + aggType: Aggregators.COUNT, + }, + ]; + const threshold: number[] = [100]; + + expect(adjustThresholdBasedOnFormat(previous, next, threshold)).toEqual(threshold); + }); +}); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/helpers/adjust_threshold_based_on_format.ts b/x-pack/plugins/observability/public/components/custom_threshold/helpers/adjust_threshold_based_on_format.ts new file mode 100644 index 0000000000000..ba4976684e386 --- /dev/null +++ b/x-pack/plugins/observability/public/components/custom_threshold/helpers/adjust_threshold_based_on_format.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 { CustomThresholdExpressionMetric } from '../../../../common/custom_threshold_rule/types'; +import { decimalToPct, pctToDecimal } from './corrected_percent_convert'; + +export const adjustThresholdBasedOnFormat = ( + previous: CustomThresholdExpressionMetric[], + next: CustomThresholdExpressionMetric[], + threshold: number[] +) => { + const isPreviousPercent = Boolean(previous.length === 1 && previous[0].field?.endsWith('.pct')); + const isPercent = Boolean(next.length === 1 && next[0].field?.endsWith('.pct')); + return isPercent === isPreviousPercent + ? threshold + : isPercent + ? threshold.map((v: number) => pctToDecimal(v)) + : isPreviousPercent + ? threshold.map((v: number) => decimalToPct(v)) + : threshold; +}; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_chart_data.ts b/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_chart_data.ts index 1f135473c0866..65bb84defa03e 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_chart_data.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_chart_data.ts @@ -52,11 +52,10 @@ export const useExpressionChartData = ( equation: expression.equation, }, ], - aggregation: expression.aggType || 'custom', + aggregation: 'custom', }), // eslint-disable-next-line react-hooks/exhaustive-deps [ - expression.aggType, expression.equation, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify(expression.metrics), diff --git a/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index f93bac2b0f104..4cae955ce9801 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -6,7 +6,6 @@ */ import { v4 as uuidv4 } from 'uuid'; -import { CUSTOM_AGGREGATOR } from '../../../../common/custom_threshold_rule/constants'; import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; import { CustomThresholdAlert, CustomThresholdRule } from '../components/alert_details_app_section'; @@ -60,7 +59,6 @@ export const buildCustomThresholdRule = ( params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -73,7 +71,6 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -89,7 +86,6 @@ export const buildCustomThresholdRule = ( warningThreshold: [2.2], }, { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -155,7 +151,6 @@ export const buildCustomThresholdAlert = ( 'kibana.alert.rule.parameters': { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -169,7 +164,6 @@ export const buildCustomThresholdAlert = ( timeUnit: 'm', }, { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { diff --git a/x-pack/plugins/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability/public/components/custom_threshold/types.ts index e726a8fd01327..dc0ad4eb71627 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/types.ts @@ -74,8 +74,6 @@ export interface InfraClientStartDeps { uiActions: UiActionsStart; unifiedSearch: UnifiedSearchPublicPluginStart; usageCollection: UsageCollectionStart; - // TODO:: check if needed => https://github.com/elastic/kibana/issues/159340 - // telemetry: ITelemetryClient; } export type RendererResult = React.ReactElement | null; diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx index 485355a278721..76a1a0efef112 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_active_alerts_badge.tsx @@ -14,11 +14,12 @@ import { paths } from '../../../../common/locators/paths'; import { useKibana } from '../../../utils/kibana_react'; export interface Props { + viewMode?: 'compact' | 'default'; activeAlerts?: number; slo: SLOWithSummaryResponse; } -export function SloActiveAlertsBadge({ slo, activeAlerts }: Props) { +export function SloActiveAlertsBadge({ slo, activeAlerts, viewMode = 'default' }: Props) { const { application: { navigateToUrl }, http: { basePath }, @@ -50,10 +51,12 @@ export function SloActiveAlertsBadge({ slo, activeAlerts }: Props) { )} data-test-subj="o11ySloActiveAlertsBadge" > - {i18n.translate('xpack.observability.slo.slo.activeAlertsBadge.label', { - defaultMessage: '{count, plural, one {# alert} other {# alerts}}', - values: { count: activeAlerts }, - })} + {viewMode !== 'default' + ? activeAlerts + : i18n.translate('xpack.observability.slo.slo.activeAlertsBadge.label', { + defaultMessage: '{count, plural, one {# alert} other {# alerts}}', + values: { count: activeAlerts }, + })} ); diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx index 455d6d9d24ed3..f79b700ed9be5 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx @@ -5,24 +5,25 @@ * 2.0. */ -import { EuiBadge, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiBadgeProps, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; -import { euiLightVars } from '@kbn/ui-theme'; import React from 'react'; +import { euiLightVars } from '@kbn/ui-theme'; export interface Props { + color?: EuiBadgeProps['color']; slo: SLOWithSummaryResponse; } -export function SloGroupByBadge({ slo }: Props) { +export function SloGroupByBadge({ slo, color }: Props) { if (!slo.groupBy || slo.groupBy === ALL_VALUE) { return null; } return ( - + ({}); - const isExploratoryView = useRouteMatch('/exploratory-view'); - useEffect( () => { - if (!isExploratoryView) - asyncForEach(apps, async (app) => { - try { - const updateState = ({ - hasData, - indices, - serviceName, - }: { - hasData?: boolean; - serviceName?: string; - indices?: string | ApmIndicesConfig; - }) => { - setHasDataMap((prevState) => ({ - ...prevState, - [app]: { - hasData, - ...(serviceName ? { serviceName } : {}), - ...(indices ? { indices } : {}), - status: FETCH_STATUS.SUCCESS, - }, - })); - }; - switch (app) { - case UX_APP: - const params = { absoluteTime: { start: absoluteStart!, end: absoluteEnd! } }; - const resultUx = await getDataHandler(app)?.hasData(params); - updateState({ - hasData: resultUx?.hasData, - indices: resultUx?.indices, - serviceName: resultUx?.serviceName as string, - }); - break; - case UPTIME_APP: - const resultSy = await getDataHandler(app)?.hasData(); - updateState({ hasData: resultSy?.hasData, indices: resultSy?.indices }); - - break; - case APM_APP: - const resultApm = await getDataHandler(app)?.hasData(); - updateState({ hasData: resultApm?.hasData, indices: resultApm?.indices }); - - break; - case INFRA_LOGS_APP: - const resultInfraLogs = await getDataHandler(app)?.hasData(); - updateState({ - hasData: resultInfraLogs?.hasData, - indices: resultInfraLogs?.indices, - }); - break; - case INFRA_METRICS_APP: - const resultInfraMetrics = await getDataHandler(app)?.hasData(); - updateState({ - hasData: resultInfraMetrics?.hasData, - indices: resultInfraMetrics?.indices, - }); - break; - case UNIVERSAL_PROFILING_APP: - // Profiling only shows the empty section for now - updateState({ hasData: false }); - break; - } - } catch (e) { + asyncForEach(apps, async (app) => { + try { + const updateState = ({ + hasData, + indices, + serviceName, + }: { + hasData?: boolean; + serviceName?: string; + indices?: string | ApmIndicesConfig; + }) => { setHasDataMap((prevState) => ({ ...prevState, [app]: { - hasData: undefined, - status: FETCH_STATUS.FAILURE, + hasData, + ...(serviceName ? { serviceName } : {}), + ...(indices ? { indices } : {}), + status: FETCH_STATUS.SUCCESS, }, })); + }; + switch (app) { + case UX_APP: + const params = { absoluteTime: { start: absoluteStart!, end: absoluteEnd! } }; + const resultUx = await getDataHandler(app)?.hasData(params); + updateState({ + hasData: resultUx?.hasData, + indices: resultUx?.indices, + serviceName: resultUx?.serviceName as string, + }); + break; + case UPTIME_APP: + const resultSy = await getDataHandler(app)?.hasData(); + updateState({ hasData: resultSy?.hasData, indices: resultSy?.indices }); + + break; + case APM_APP: + const resultApm = await getDataHandler(app)?.hasData(); + updateState({ hasData: resultApm?.hasData, indices: resultApm?.indices }); + + break; + case INFRA_LOGS_APP: + const resultInfraLogs = await getDataHandler(app)?.hasData(); + updateState({ + hasData: resultInfraLogs?.hasData, + indices: resultInfraLogs?.indices, + }); + break; + case INFRA_METRICS_APP: + const resultInfraMetrics = await getDataHandler(app)?.hasData(); + updateState({ + hasData: resultInfraMetrics?.hasData, + indices: resultInfraMetrics?.indices, + }); + break; + case UNIVERSAL_PROFILING_APP: + // Profiling only shows the empty section for now + updateState({ hasData: false }); + break; } - }); + } catch (e) { + setHasDataMap((prevState) => ({ + ...prevState, + [app]: { + hasData: undefined, + status: FETCH_STATUS.FAILURE, + }, + })); + } + }); }, // eslint-disable-next-line react-hooks/exhaustive-deps - [isExploratoryView] + [] ); useEffect(() => { diff --git a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx deleted file mode 100644 index ea80fc8f8cc1c..0000000000000 --- a/x-pack/plugins/observability/public/hooks/use_get_user_cases_permissions.tsx +++ /dev/null @@ -1,50 +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 { useEffect, useState } from 'react'; -import { CasesPermissions } from '@kbn/cases-plugin/common'; -import { useKibana } from '../utils/kibana_react'; -import { casesFeatureId } from '../../common'; - -export function useGetUserCasesPermissions() { - const [casesPermissions, setCasesPermissions] = useState({ - all: false, - read: false, - create: false, - update: false, - delete: false, - push: false, - connectors: false, - }); - const uiCapabilities = useKibana().services.application.capabilities; - - const casesCapabilities = useKibana().services.cases.helpers.getUICapabilities( - uiCapabilities[casesFeatureId] - ); - - useEffect(() => { - setCasesPermissions({ - all: casesCapabilities.all, - create: casesCapabilities.create, - read: casesCapabilities.read, - update: casesCapabilities.update, - delete: casesCapabilities.delete, - push: casesCapabilities.push, - connectors: casesCapabilities.connectors, - }); - }, [ - casesCapabilities.all, - casesCapabilities.create, - casesCapabilities.read, - casesCapabilities.update, - casesCapabilities.delete, - casesCapabilities.push, - casesCapabilities.connectors, - ]); - - return casesPermissions; -} diff --git a/x-pack/plugins/observability/public/locators/slo_list.test.ts b/x-pack/plugins/observability/public/locators/slo_list.test.ts index f12bab21f6dd4..68bf2c2589fea 100644 --- a/x-pack/plugins/observability/public/locators/slo_list.test.ts +++ b/x-pack/plugins/observability/public/locators/slo_list.test.ts @@ -14,7 +14,7 @@ describe('SloListLocator', () => { const location = await locator.getLocation({}); expect(location.app).toEqual('observability'); expect(location.path).toEqual( - "/slos?search=(kqlQuery:'',page:0,sort:(by:status,direction:desc))" + "/slos?search=(kqlQuery:'',page:0,sort:(by:status,direction:desc),viewMode:compact)" ); }); @@ -24,7 +24,7 @@ describe('SloListLocator', () => { }); expect(location.app).toEqual('observability'); expect(location.path).toEqual( - "/slos?search=(kqlQuery:'slo.name:%20%22Service%20Availability%22%20and%20slo.indicator.type%20:%20%22sli.kql.custom%22',page:0,sort:(by:status,direction:desc))" + "/slos?search=(kqlQuery:'slo.name:%20%22Service%20Availability%22%20and%20slo.indicator.type%20:%20%22sli.kql.custom%22',page:0,sort:(by:status,direction:desc),viewMode:compact)" ); }); }); diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx index dfb1f571b6d43..756f90ecd97cf 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx @@ -76,16 +76,6 @@ jest.mock('../../hooks/use_fetch_rule', () => { }; }); jest.mock('@kbn/observability-shared-plugin/public'); -jest.mock('../../hooks/use_get_user_cases_permissions', () => ({ - useGetUserCasesPermissions: () => ({ - all: true, - create: true, - delete: true, - push: true, - read: true, - update: true, - }), -})); const useFetchAlertDetailMock = useFetchAlertDetail as jest.Mock; const useParamsMock = useParams as jest.Mock; diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx index a7661ba43fddb..06da477896c5d 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx @@ -58,7 +58,7 @@ export function AlertDetails() { const [isLoading, alert] = useFetchAlertDetail(alertId); const [ruleTypeModel, setRuleTypeModel] = useState(null); const CasesContext = getCasesContext(); - const userCasesPermissions = canUseCases(); + const userCasesPermissions = canUseCases([observabilityFeatureId]); const { rule } = useFetchRule({ ruleId: alert?.fields[ALERT_RULE_UUID], }); diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx index 9e80d01ed82e4..c1b1493daa3d9 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx @@ -19,12 +19,10 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; import { rulesLocatorID } from '../../../common'; import { RulesParams } from '../../locators/rules'; import { useKibana } from '../../utils/kibana_react'; -import { useHasData } from '../../hooks/use_has_data'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useTimeBuckets } from '../../hooks/use_time_buckets'; import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types'; import { useToasts } from '../../hooks/use_toast'; -import { LoadingObservability } from '../../components/loading_observability'; import { renderRuleStats, RuleStatsState } from './components/rule_stats'; import { ObservabilityAlertSearchBar } from '../../components/alert_search_bar/alert_search_bar'; import { @@ -94,7 +92,6 @@ function InternalAlertsPage() { error: 0, snoozed: 0, }); - const { hasAnyData, isAllRequestsComplete } = useHasData(); const [esQuery, setEsQuery] = useState<{ bool: BoolQuery }>(); const timeBuckets = useTimeBuckets(); const bucketSize = useMemo( @@ -173,10 +170,6 @@ function InternalAlertsPage() { const manageRulesHref = http.basePath.prepend('/app/observability/alerts/rules'); - if (!hasAnyData && !isAllRequestsComplete) { - return ; - } - return ( ({ __esModule: true, useKibana: jest.fn(() => mockUseKibanaReturnValue), })); -jest.mock('../../../hooks/use_get_user_cases_permissions', () => ({ - useGetUserCasesPermissions: jest.fn(() => ({ create: true, read: true })), -})); - jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react', () => ({ useKibana: jest.fn(() => ({ services: { notifications: { toasts: { addDanger: jest.fn(), addSuccess: jest.fn() } } }, @@ -175,4 +174,18 @@ describe('ObservabilityActions component', () => { expect(refresh).toHaveBeenCalled(); }); + + it('should hide the case actions without permissions', async () => { + mockUseKibanaReturnValue.services.cases.helpers.canUseCases.mockReturnValue( + noCasesPermissions() + ); + + const wrapper = await setup('nothing'); + wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click'); + + expect(wrapper.find('[data-test-subj="add-to-new-case-action"]').hostNodes().length).toBe(0); + expect(wrapper.find('[data-test-subj="add-to-existing-case-action"]').hostNodes().length).toBe( + 0 + ); + }); }); diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx index 7799655908907..9a3bce0a9a326 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx @@ -30,12 +30,11 @@ import { } from '@kbn/rule-data-utils'; import { useBulkUntrackAlerts } from '@kbn/triggers-actions-ui-plugin/public'; import { useKibana } from '../../../utils/kibana_react'; -import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions'; import { isAlertDetailsEnabledPerApp } from '../../../utils/is_alert_details_enabled'; import { parseAlert } from '../helpers/parse_alert'; import { paths } from '../../../../common/locators/paths'; import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants'; -import type { ObservabilityRuleTypeRegistry } from '../../..'; +import { observabilityFeatureId, ObservabilityRuleTypeRegistry } from '../../..'; import type { ConfigSchema } from '../../../plugin'; import type { TopAlert } from '../../../typings/alerts'; @@ -62,15 +61,15 @@ export function AlertActions({ }: Props) { const { cases: { - helpers: { getRuleIdFromEvent }, + helpers: { getRuleIdFromEvent, canUseCases }, hooks: { useCasesAddToNewCaseFlyout, useCasesAddToExistingCaseModal }, }, http: { basePath: { prepend }, }, } = useKibana().services; - const userCasesPermissions = useGetUserCasesPermissions(); const { mutateAsync: untrackAlerts } = useBulkUntrackAlerts(); + const userCasesPermissions = canUseCases([observabilityFeatureId]); const parseObservabilityAlert = useMemo( () => parseAlert(observabilityRuleTypeRegistry), diff --git a/x-pack/plugins/observability/public/pages/cases/cases.tsx b/x-pack/plugins/observability/public/pages/cases/cases.tsx index 5f746bcc4d490..13fc73e909601 100644 --- a/x-pack/plugins/observability/public/pages/cases/cases.tsx +++ b/x-pack/plugins/observability/public/pages/cases/cases.tsx @@ -7,23 +7,17 @@ import React from 'react'; -import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions'; +import { observabilityFeatureId } from '../../../common'; import { usePluginContext } from '../../hooks/use_plugin_context'; -import { useHasData } from '../../hooks/use_has_data'; import { Cases } from './components/cases'; -import { LoadingObservability } from '../../components/loading_observability'; import { CaseFeatureNoPermissions } from './components/feature_no_permissions'; import { HeaderMenu } from '../overview/components/header_menu/header_menu'; +import { useKibana } from '../../utils/kibana_react'; export function CasesPage() { - const userCasesPermissions = useGetUserCasesPermissions(); const { ObservabilityPageTemplate } = usePluginContext(); - - const { hasAnyData, isAllRequestsComplete } = useHasData(); - - if (!hasAnyData && !isAllRequestsComplete) { - return ; - } + const { canUseCases } = useKibana().services.cases.helpers; + const userCasesPermissions = canUseCases([observabilityFeatureId]); return userCasesPermissions.read ? ( diff --git a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx index d0fc1d01734f2..695165d05e1bd 100644 --- a/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx +++ b/x-pack/plugins/observability/public/pages/cases/components/cases.stories.tsx @@ -27,6 +27,7 @@ const defaultProps: CasesProps = { push: true, update: true, connectors: true, + settings: true, }, }; @@ -43,5 +44,6 @@ CasesPageWithNoPermissions.args = { push: false, update: false, connectors: false, + settings: false, }, }; diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx index b8c18aa457b48..615edf3ef65f3 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx @@ -33,13 +33,14 @@ export interface Props { export function HeaderControl({ isLoading, slo }: Props) { const { - application: { navigateToUrl }, + application: { navigateToUrl, capabilities }, http: { basePath }, share: { url: { locators }, }, triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout }, } = useKibana().services; + const hasApmReadCapabilities = capabilities.apm.show; const { hasWriteCapabilities } = useCapabilities(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -202,6 +203,7 @@ export function HeaderControl({ isLoading, slo }: Props) { diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx index f31d36c822264..92ea62f385ab8 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; +import type { Capabilities } from '@kbn/core/public'; import { useKibana } from '../../utils/kibana_react'; import { useParams, useLocation } from 'react-router-dom'; @@ -60,6 +61,9 @@ const mockNavigate = jest.fn(); const mockLocator = jest.fn(); const mockClone = jest.fn(); const mockDelete = jest.fn(); +const mockCapabilities = { + apm: { show: true }, +} as unknown as Capabilities; const mockKibana = () => { useKibanaMock.mockReturnValue({ @@ -68,7 +72,7 @@ const mockKibana = () => { lens: { EmbeddableComponent: () =>
    mocked component
    , }, - application: { navigateToUrl: mockNavigate }, + application: { navigateToUrl: mockNavigate, capabilities: mockCapabilities }, charts: chartPluginMock.createStartContract(), http: { basePath: { diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx index 267ebac0ce9b4..67869e7f0e76e 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_badges.stories.tsx @@ -11,7 +11,7 @@ import { ComponentStory } from '@storybook/react'; import { EuiFlexGroup } from '@elastic/eui'; import { buildForecastedSlo } from '../../../../data/slo/slo'; import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; -import { SloBadges as Component, Props } from './slo_badges'; +import { SloBadges as Component, SloBadgesProps } from './slo_badges'; export default { component: Component, @@ -19,7 +19,7 @@ export default { decorators: [KibanaReactStorybookDecorator], }; -const Template: ComponentStory = (props: Props) => ( +const Template: ComponentStory = (props: SloBadgesProps) => ( 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 deccd010205a0..9ff1e3c14a2b2 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 @@ -17,8 +17,9 @@ import { SloTimeWindowBadge } from './slo_time_window_badge'; import { SloRulesBadge } from './slo_rules_badge'; import type { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; import { SloGroupByBadge } from '../../../../components/slo/slo_status_badge/slo_group_by_badge'; +export type ViewMode = 'default' | 'compact'; -export interface Props { +export interface SloBadgesProps { activeAlerts?: number; isLoading: boolean; rules: Array> | undefined; @@ -26,33 +27,17 @@ export interface Props { onClickRuleBadge: () => void; } -export function SloBadges({ activeAlerts, isLoading, rules, slo, onClickRuleBadge }: Props) { +export function SloBadges({ + activeAlerts, + isLoading, + rules, + slo, + onClickRuleBadge, +}: SloBadgesProps) { return ( {isLoading ? ( - <> - - - - + ) : ( <> @@ -66,3 +51,31 @@ export function SloBadges({ activeAlerts, isLoading, rules, slo, onClickRuleBadg ); } + +export function LoadingBadges() { + return ( + <> + + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx index ad73af3d73bfa..c85eb6776680b 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_indicator_type_badge.tsx @@ -6,21 +6,22 @@ */ import React from 'react'; -import { EuiBadge, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiFlexItem, EuiToolTip, EuiBadgeProps } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; -import { euiLightVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; +import { euiLightVars } from '@kbn/ui-theme'; import { useKibana } from '../../../../utils/kibana_react'; import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; import { isApmIndicatorType } from '../../../../utils/slo/indicator'; import { toIndicatorTypeLabel } from '../../../../utils/slo/labels'; export interface Props { + color?: EuiBadgeProps['color']; slo: SLOWithSummaryResponse; } -export function SloIndicatorTypeBadge({ slo }: Props) { +export function SloIndicatorTypeBadge({ slo, color }: Props) { const { application: { navigateToUrl }, http: { basePath }, @@ -54,7 +55,7 @@ export function SloIndicatorTypeBadge({ slo }: Props) { return ( <> - + {toIndicatorTypeLabel(slo.indicator.type)} @@ -68,7 +69,7 @@ export function SloIndicatorTypeBadge({ slo }: Props) { })} > - +
    ); diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx index d218eeda7f0ed..b5d4ecd0224fe 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiBadge, EuiFlexItem } from '@elastic/eui'; +import { EuiBadge, EuiBadgeProps, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { rollingTimeWindowTypeSchema, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { euiLightVars } from '@kbn/ui-theme'; @@ -15,16 +15,17 @@ import { toCalendarAlignedMomentUnitOfTime } from '../../../../utils/slo/duratio import { toDurationLabel } from '../../../../utils/slo/labels'; export interface Props { + color?: EuiBadgeProps['color']; slo: SLOWithSummaryResponse; } -export function SloTimeWindowBadge({ slo }: Props) { +export function SloTimeWindowBadge({ slo, color }: Props) { const unit = slo.timeWindow.duration.slice(-1); if (rollingTimeWindowTypeSchema.is(slo.timeWindow.type)) { return ( @@ -45,7 +46,7 @@ export function SloTimeWindowBadge({ slo }: Props) { return ( - + {i18n.translate('xpack.observability.slo.slo.timeWindow.calendar', { defaultMessage: '{elapsed}/{total} days', values: { diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/cards_per_row.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/cards_per_row.tsx new file mode 100644 index 0000000000000..04e787d7b3536 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/cards_per_row.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; + +export const SLO_CARD_VIEW_PER_ROW_SIZE = 'slo-card-view-per-row-size'; + +export function CardsPerRow({ + setCardsPerRow, +}: { + setCardsPerRow: (cardsPerRow?: string) => void; +}) { + const [value, setValue] = useLocalStorage(SLO_CARD_VIEW_PER_ROW_SIZE, '3'); + + useEffect(() => { + setCardsPerRow(value); + }, [setCardsPerRow, value]); + + const options = [ + { value: '3', text: '3' }, + { value: '4', text: '4' }, + ]; + + return ( + + } + > + setValue(e.target.value)} + /> + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx new file mode 100644 index 0000000000000..07ad3caf1fd00 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx @@ -0,0 +1,181 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + Chart, + isMetricElementEvent, + Metric, + Settings, + DARK_THEME, + MetricTrendShape, +} from '@elastic/charts'; +import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; +import { SLOWithSummaryResponse, HistoricalSummaryResponse, ALL_VALUE } from '@kbn/slo-schema'; +import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { useSloListActions } from '../../hooks/use_slo_list_actions'; +import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout'; +import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter'; +import { useKibana } from '../../../../utils/kibana_react'; +import { useSloFormattedSummary } from '../../hooks/use_slo_summary'; +import { SloCardItemActions } from './slo_card_item_actions'; +import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; +import { SloDeleteConfirmationModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; +import { SloCardItemBadges } from './slo_card_item_badges'; + +export interface Props { + slo: SLOWithSummaryResponse; + rules: Array> | undefined; + historicalSummary?: HistoricalSummaryResponse[]; + historicalSummaryLoading: boolean; + activeAlerts?: number; + loading: boolean; + error: boolean; + cardsPerRow: number; +} + +const useCardColor = (status?: SLOWithSummaryResponse['summary']['status']) => { + const colors = { + DEGRADING: useEuiBackgroundColor('warning'), + VIOLATED: useEuiBackgroundColor('danger'), + HEALTHY: useEuiBackgroundColor('success'), + NO_DATA: useEuiBackgroundColor('subdued'), + }; + + return colors[status ?? 'NO_DATA']; +}; + +const getSubTitle = (slo: SLOWithSummaryResponse, cardsPerRow: number) => { + const subTitle = + slo.groupBy && slo.groupBy !== ALL_VALUE ? `${slo.groupBy}: ${slo.instanceId}` : ''; + if (cardsPerRow === 4) { + return subTitle.substring(0, 30) + (subTitle.length > 30 ? '..' : ''); + } + return subTitle.substring(0, 40) + (subTitle.length > 40 ? '..' : ''); +}; + +export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cardsPerRow }: Props) { + const { + application: { navigateToUrl }, + } = useKibana().services; + + const [isMouseOver, setIsMouseOver] = useState(false); + const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); + const [isAddRuleFlyoutOpen, setIsAddRuleFlyoutOpen] = useState(false); + const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); + + const { sliValue, sloTarget, sloDetailsUrl } = useSloFormattedSummary(slo); + + const cardColor = useCardColor(slo.summary.status); + + const subTitle = getSubTitle(slo, cardsPerRow); + + const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value'); + + const { handleCreateRule, handleDeleteCancel, handleDeleteConfirm } = useSloListActions({ + slo, + setDeleteConfirmationModalOpen, + setIsActionsPopoverOpen, + setIsAddRuleFlyoutOpen, + }); + + return ( + <> + { + if (!isMouseOver) { + setIsMouseOver(true); + } + }} + onMouseLeave={() => { + if (isMouseOver) { + setIsMouseOver(false); + } + }} + paddingSize="none" + style={{ + height: '182px', + overflow: 'hidden', + position: 'relative', + }} + title={slo.summary.status} + > + + { + if (isMetricElementEvent(d)) { + navigateToUrl(sloDetailsUrl); + } + }} + locale={i18n.getLocale()} + /> + ({ + x: d.key as number, + y: d.value as number, + })), + extra: ( + + ), + icon: () => , + color: cardColor, + }, + ], + ]} + /> + + + {(isMouseOver || isActionsPopoverOpen) && ( + + )} + + + + + {isDeleteConfirmationModalOpen ? ( + + ) : null} + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx new file mode 100644 index 0000000000000..51d1887d433fb --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import styled from 'styled-components'; +import { useEuiShadow } from '@elastic/eui'; +import { SloItemActions } from '../slo_item_actions'; + +type PopoverPosition = 'relative' | 'default'; + +interface ActionContainerProps { + boxShadow: string; + position: PopoverPosition; +} + +const Container = styled.div` + ${({ position }) => + position === 'relative' + ? // custom styles used to overlay the popover button on `MetricItem` + ` + display: inline-block; + position: relative; + bottom: 42px; + left: 12px; + z-index: 1; +` + : // otherwise, no custom position needed + ''} + + border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + ${({ boxShadow, position }) => (position === 'relative' ? boxShadow : '')} +`; + +interface Props { + slo: SLOWithSummaryResponse; + isActionsPopoverOpen: boolean; + setIsActionsPopoverOpen: (value: boolean) => void; + setDeleteConfirmationModalOpen: (value: boolean) => void; + setIsAddRuleFlyoutOpen: (value: boolean) => void; +} + +export function SloCardItemActions(props: Props) { + const euiShadow = useEuiShadow('l'); + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx new file mode 100644 index 0000000000000..06bc6cf19e0c9 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_badges.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import React from 'react'; +import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; +import styled from 'styled-components'; +import { EuiFlexGroup } from '@elastic/eui'; +import { LoadingBadges } from '../badges/slo_badges'; +import { SloIndicatorTypeBadge } from '../badges/slo_indicator_type_badge'; +import { SloTimeWindowBadge } from '../badges/slo_time_window_badge'; +import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge'; +import { SloRulesBadge } from '../badges/slo_rules_badge'; +import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; + +interface Props { + hasGroupBy: boolean; + activeAlerts?: number; + slo: SLOWithSummaryResponse; + rules: Array> | undefined; + handleCreateRule: () => void; +} + +const Container = styled.div<{ hasGroupBy: boolean }>` + position: absolute; + display: inline-block; + top: ${({ hasGroupBy }) => (hasGroupBy ? '55px' : '35px')}; + left: 7px; + z-index: 1; + border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; +`; + +export function SloCardItemBadges({ + slo, + activeAlerts, + rules, + handleCreateRule, + hasGroupBy, +}: Props) { + return ( + + + {!slo.summary ? ( + + ) : ( + <> + + + + + + )} + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx new file mode 100644 index 0000000000000..3768bdbb7dac3 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGrid, EuiFlexItem, EuiPanel, EuiSkeletonText } from '@elastic/eui'; +import { SLOWithSummaryResponse, ALL_VALUE } from '@kbn/slo-schema'; +import { EuiFlexGridProps } from '@elastic/eui/src/components/flex/flex_grid'; +import { ActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; +import type { UseFetchRulesForSloResponse } from '../../../../hooks/slo/use_fetch_rules_for_slo'; +import { useFetchHistoricalSummary } from '../../../../hooks/slo/use_fetch_historical_summary'; +import { SloCardItem } from './slo_card_item'; + +export interface Props { + sloList: SLOWithSummaryResponse[]; + loading: boolean; + error: boolean; + cardsPerRow?: string; + activeAlertsBySlo: ActiveAlerts; + rulesBySlo?: UseFetchRulesForSloResponse['data']; +} + +export function SloListCardView({ + sloList, + loading, + error, + cardsPerRow, + rulesBySlo, + activeAlertsBySlo, +}: Props) { + const { isLoading: historicalSummaryLoading, data: historicalSummaries = [] } = + useFetchHistoricalSummary({ + list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })), + }); + + if (loading && sloList.length === 0) { + return ; + } + + return ( + + {sloList.map((slo) => ( + + + historicalSummary.sloId === slo.id && + historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + )?.data + } + historicalSummaryLoading={historicalSummaryLoading} + cardsPerRow={Number(cardsPerRow)} + /> + + ))} + + ); +} + +function LoadingSloGrid({ gridSize }: { gridSize: number }) { + const ROWS = 4; + const COLUMNS = gridSize; + const loaders = Array(ROWS * COLUMNS).fill(null); + return ( + <> + + {loaders.map((_, i) => ( + + + + {' '} + + ))} + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx b/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx new file mode 100644 index 0000000000000..a02730231ae5f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { useQueryClient } from '@tanstack/react-query'; +import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types'; +import { sloKeys } from '../../../../hooks/slo/query_key_factory'; +import { useKibana } from '../../../../utils/kibana_react'; +import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants'; +import { sloFeatureId } from '../../../../../common'; + +export function BurnRateRuleFlyout({ + slo, + isAddRuleFlyoutOpen, + setIsAddRuleFlyoutOpen, +}: { + slo: SLOWithSummaryResponse; + isAddRuleFlyoutOpen: boolean; + setIsAddRuleFlyoutOpen: (value: boolean) => void; +}) { + const { + triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout }, + } = useKibana().services; + + const filteredRuleTypes = useGetFilteredRuleTypes(); + + const queryClient = useQueryClient(); + + const handleSavedRule = async () => { + queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false }); + }; + + return isAddRuleFlyoutOpen ? ( + { + setIsAddRuleFlyoutOpen(false); + }} + useRuleProducer + /> + ) : null; +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_item_actions.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_item_actions.tsx new file mode 100644 index 0000000000000..4fb03968d40a0 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_item_actions.tsx @@ -0,0 +1,217 @@ +/* + * Copyright 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 { + EuiButtonIcon, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiPopover, + EuiButtonIconProps, + useEuiShadow, + EuiPanel, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import styled from 'styled-components'; +import { useCapabilities } from '../../../hooks/slo/use_capabilities'; +import { useCloneSlo } from '../../../hooks/slo/use_clone_slo'; +import { useKibana } from '../../../utils/kibana_react'; +import { paths } from '../../../../common/locators/paths'; +import { RulesParams } from '../../../locators/rules'; +import { rulesLocatorID } from '../../../../common'; +import { + transformCreateSLOFormToCreateSLOInput, + transformSloResponseToCreateSloForm, +} from '../../slo_edit/helpers/process_slo_form_values'; + +interface Props { + slo: SLOWithSummaryResponse; + isActionsPopoverOpen: boolean; + setIsActionsPopoverOpen: (value: boolean) => void; + setDeleteConfirmationModalOpen: (value: boolean) => void; + setIsAddRuleFlyoutOpen: (value: boolean) => void; + btnProps?: Partial; +} +const CustomShadowPanel = styled(EuiPanel)<{ shadow: string }>` + ${(props) => props.shadow} +`; + +function IconPanel({ children, hasPanel }: { children: JSX.Element; hasPanel: boolean }) { + const shadow = useEuiShadow('s'); + if (!hasPanel) return children; + return ( + + {children} + + ); +} + +export function SloItemActions({ + slo, + isActionsPopoverOpen, + setIsActionsPopoverOpen, + setIsAddRuleFlyoutOpen, + setDeleteConfirmationModalOpen, + btnProps, +}: Props) { + const { + application: { navigateToUrl }, + http: { basePath }, + share: { + url: { locators }, + }, + } = useKibana().services; + const { hasWriteCapabilities } = useCapabilities(); + const { mutate: cloneSlo } = useCloneSlo(); + + const sloDetailsUrl = basePath.prepend( + paths.observability.sloDetails( + slo.id, + slo.groupBy !== ALL_VALUE && slo.instanceId ? slo.instanceId : undefined + ) + ); + + const handleClickActions = () => { + setIsActionsPopoverOpen(!isActionsPopoverOpen); + }; + + const handleViewDetails = () => { + navigateToUrl(sloDetailsUrl); + }; + + const handleEdit = () => { + navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id))); + }; + + const handleNavigateToRules = async () => { + const locator = locators.get(rulesLocatorID); + locator?.navigate({ params: { sloId: slo.id } }, { replace: false }); + }; + + const handleClone = () => { + const newSlo = transformCreateSLOFormToCreateSLOInput( + transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })! + ); + + cloneSlo({ slo: newSlo, originalSloId: slo.id }); + setIsActionsPopoverOpen(false); + }; + + const handleDelete = () => { + setDeleteConfirmationModalOpen(true); + setIsActionsPopoverOpen(false); + }; + + const handleCreateRule = () => { + setIsActionsPopoverOpen(false); + setIsAddRuleFlyoutOpen(true); + }; + + const btn = ( + + ); + + return ( + {btn} : btn} + panelPaddingSize="m" + closePopover={handleClickActions} + isOpen={isActionsPopoverOpen} + > + + {i18n.translate('xpack.observability.slo.item.actions.details', { + defaultMessage: 'Details', + })} + , + + {i18n.translate('xpack.observability.slo.item.actions.edit', { + defaultMessage: 'Edit', + })} + , + + {i18n.translate('xpack.observability.slo.item.actions.createRule', { + defaultMessage: 'Create new alert rule', + })} + , + + {i18n.translate('xpack.observability.slo.item.actions.manageRules', { + defaultMessage: 'Manage rules', + })} + , + + {i18n.translate('xpack.observability.slo.item.actions.clone', { + defaultMessage: 'Clone', + })} + , + + {i18n.translate('xpack.observability.slo.item.actions.delete', { + defaultMessage: 'Delete', + })} + , + ]} + /> + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx index 380d0100db1a1..ee1fff8e17c1d 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list.tsx @@ -8,9 +8,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiPagination } from '@elastic/eui'; import { useIsMutating } from '@tanstack/react-query'; import React, { useState } from 'react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { SlosView } from './slos_view'; +import { SLO_CARD_VIEW_PER_ROW_SIZE } from './card_view/cards_per_row'; +import { SLOViewType, ToggleSLOView } from './toggle_slo_view'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; import { useUrlSearchState } from '../hooks/use_url_search_state'; -import { SloListItems } from './slo_list_items'; import { SloListSearchBar, SortField } from './slo_list_search_bar'; export interface Props { @@ -24,6 +27,8 @@ export function SloList({ autoRefresh }: Props) { const [sort, setSort] = useState(state.sort.by); const [direction] = useState<'asc' | 'desc'>(state.sort.direction); + const [sloView, setSLOView] = useState('cardView'); + const { isLoading, isRefetching, @@ -43,6 +48,7 @@ export function SloList({ autoRefresh }: Props) { const isCloningSlo = Boolean(useIsMutating(['cloningSlo'])); const isUpdatingSlo = Boolean(useIsMutating(['updatingSlo'])); const isDeletingSlo = Boolean(useIsMutating(['deleteSlo'])); + const [cardsPerRow, setCardsPerRow] = useLocalStorage(SLO_CARD_VIEW_PER_ROW_SIZE, '4'); const handlePageClick = (pageNumber: number) => { setPage(pageNumber); @@ -71,9 +77,16 @@ export function SloList({ autoRefresh }: Props) { initialState={state} /> - - + + + {total > 0 ? ( 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 ae843977a0ee7..90757be5dc7a9 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 @@ -5,37 +5,16 @@ * 2.0. */ -import { - EuiButtonIcon, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiPopover, - EuiText, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import type { Rule } from '@kbn/triggers-actions-ui-plugin/public'; -import { useQueryClient } from '@tanstack/react-query'; import React, { useState } from 'react'; +import { useSloFormattedSummary } from '../hooks/use_slo_summary'; +import { BurnRateRuleFlyout } from './common/burn_rate_rule_flyout'; +import { useSloListActions } from '../hooks/use_slo_list_actions'; +import { SloItemActions } from './slo_item_actions'; import { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; -import { rulesLocatorID, sloFeatureId } from '../../../../common'; -import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants'; -import { paths } from '../../../../common/locators/paths'; -import { sloKeys } from '../../../hooks/slo/query_key_factory'; -import { useCapabilities } from '../../../hooks/slo/use_capabilities'; -import { useCloneSlo } from '../../../hooks/slo/use_clone_slo'; -import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo'; import type { SloRule } from '../../../hooks/slo/use_fetch_rules_for_slo'; -import { useGetFilteredRuleTypes } from '../../../hooks/use_get_filtered_rule_types'; -import type { RulesParams } from '../../../locators/rules'; -import { useKibana } from '../../../utils/kibana_react'; -import { - transformCreateSLOFormToCreateSLOInput, - transformSloResponseToCreateSloForm, -} from '../../slo_edit/helpers/process_slo_form_values'; import { SloBadges } from './badges/slo_badges'; import { SloSummary } from './slo_summary'; @@ -54,80 +33,18 @@ export function SloListItem({ historicalSummaryLoading, activeAlerts, }: SloListItemProps) { - const { - application: { navigateToUrl }, - http: { basePath }, - share: { - url: { locators }, - }, - triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout }, - } = useKibana().services; - const { hasWriteCapabilities } = useCapabilities(); - const queryClient = useQueryClient(); - - const filteredRuleTypes = useGetFilteredRuleTypes(); - - const { mutate: cloneSlo } = useCloneSlo(); - const { mutate: deleteSlo } = useDeleteSlo(); - const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); const [isAddRuleFlyoutOpen, setIsAddRuleFlyoutOpen] = useState(false); const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); - const handleClickActions = () => { - setIsActionsPopoverOpen(!isActionsPopoverOpen); - }; - - const sloDetailsUrl = basePath.prepend( - paths.observability.sloDetails( - slo.id, - slo.groupBy !== ALL_VALUE && slo.instanceId ? slo.instanceId : undefined - ) - ); - const handleViewDetails = () => { - navigateToUrl(sloDetailsUrl); - }; - - const handleEdit = () => { - navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id))); - }; - - const handleCreateRule = () => { - setIsActionsPopoverOpen(false); - setIsAddRuleFlyoutOpen(true); - }; - - const handleSavedRule = async () => { - queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false }); - }; + const { sloDetailsUrl } = useSloFormattedSummary(slo); - const handleNavigateToRules = async () => { - const locator = locators.get(rulesLocatorID); - locator?.navigate({ params: { sloId: slo.id } }, { replace: false }); - }; - - const handleClone = () => { - const newSlo = transformCreateSLOFormToCreateSLOInput( - transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })! - ); - - cloneSlo({ slo: newSlo, originalSloId: slo.id }); - setIsActionsPopoverOpen(false); - }; - - const handleDelete = () => { - setDeleteConfirmationModalOpen(true); - setIsActionsPopoverOpen(false); - }; - - const handleDeleteConfirm = () => { - setDeleteConfirmationModalOpen(false); - deleteSlo({ id: slo.id, name: slo.name }); - }; - - const handleDeleteCancel = () => { - setDeleteConfirmationModalOpen(false); - }; + const { handleCreateRule, handleDeleteCancel, handleDeleteConfirm } = useSloListActions({ + slo, + setDeleteConfirmationModalOpen, + setIsActionsPopoverOpen, + setIsAddRuleFlyoutOpen, + }); return ( @@ -172,113 +89,21 @@ export function SloListItem({ {/* ACTIONS */} - - } - panelPaddingSize="m" - closePopover={handleClickActions} - isOpen={isActionsPopoverOpen} - > - - {i18n.translate('xpack.observability.slo.item.actions.details', { - defaultMessage: 'Details', - })} - , - - {i18n.translate('xpack.observability.slo.item.actions.edit', { - defaultMessage: 'Edit', - })} - , - - {i18n.translate('xpack.observability.slo.item.actions.createRule', { - defaultMessage: 'Create new alert rule', - })} - , - - {i18n.translate('xpack.observability.slo.item.actions.manageRules', { - defaultMessage: 'Manage rules', - })} - , - - {i18n.translate('xpack.observability.slo.item.actions.clone', { - defaultMessage: 'Clone', - })} - , - - {i18n.translate('xpack.observability.slo.item.actions.delete', { - defaultMessage: 'Delete', - })} - , - ]} - /> - + - {isAddRuleFlyoutOpen ? ( - { - setIsAddRuleFlyoutOpen(false); - }} - useRuleProducer - /> - ) : null} + {isDeleteConfirmationModalOpen ? ( = (props: Props) => [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string] - ); - - const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds }); - const { data: rulesBySlo } = useFetchRulesForSlo({ - sloIds: sloIdsAndInstanceIds.map((item) => item[0]), - }); +export function SloListItems({ sloList, activeAlertsBySlo, rulesBySlo }: Props) { const { isLoading: historicalSummaryLoading, data: historicalSummaries = [] } = useFetchHistoricalSummary({ list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })), }); - if (!loading && !error && sloList.length === 0) { - return ; - } - if (!loading && error) { - return ; - } - return ( {sloList.map((slo) => ( diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx index 401118e3c8dfc..77d23d6301aed 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_summary.tsx @@ -6,13 +6,11 @@ */ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; -import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; -import { useKibana } from '../../../utils/kibana_react'; +import { useSloFormattedSummary } from '../hooks/use_slo_summary'; import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter'; -import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { SloSparkline } from './slo_sparkline'; export interface Props { @@ -22,18 +20,12 @@ export interface Props { } export function SloSummary({ slo, historicalSummary = [], historicalSummaryLoading }: Props) { - const { uiSettings } = useKibana().services; - const percentFormat = uiSettings.get('format:percent:defaultPattern'); + const { sliValue, sloTarget, errorBudgetRemaining } = useSloFormattedSummary(slo); const isSloFailed = slo.summary.status === 'VIOLATED' || slo.summary.status === 'DEGRADING'; const titleColor = isSloFailed ? 'danger' : ''; const errorBudgetBurnDownData = formatHistoricalData(historicalSummary, 'error_budget_remaining'); const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value'); - const errorBudgetRemaining = - slo.summary.errorBudget.remaining <= 0 - ? Math.trunc(slo.summary.errorBudget.remaining * 100) / 100 - : slo.summary.errorBudget.remaining; - return ( @@ -48,13 +40,9 @@ export function SloSummary({ slo, historicalSummary = [], historicalSummaryLoadi [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string] + ); + + const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds }); + const { data: rulesBySlo } = useFetchRulesForSlo({ + sloIds: sloIdsAndInstanceIds.map((item) => item[0]), + }); + + if (!loading && !error && sloList.length === 0) { + return ; + } + if (!loading && error) { + return ; + } + + return sloView === 'cardView' ? ( + + + + ) : ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx new file mode 100644 index 0000000000000..43b5d849b2e0b --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx @@ -0,0 +1,97 @@ +/* + * Copyright 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 { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonGroup, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiPopoverTitle, +} from '@elastic/eui'; +import { CardsPerRow } from './card_view/cards_per_row'; + +export type SLOViewType = 'cardView' | 'listView'; + +interface Props { + setCardsPerRow: (gridSize?: string) => void; + setSLOView: (view: SLOViewType) => void; + sloView: SLOViewType; +} +const toggleButtonsIcons = [ + { + id: `cardView`, + label: 'Card View', + iconType: 'visGauge', + 'data-test-subj': 'sloCardViewButton', + }, + { + id: `listView`, + label: 'List View', + iconType: 'list', + 'data-test-subj': 'sloListViewButton', + }, +]; + +export function ToggleSLOView({ sloView, setSLOView, setCardsPerRow }: Props) { + return ( + + + setSLOView(id as SLOViewType)} + isIconOnly + /> + + {sloView === 'cardView' && ( + + + + )} + + ); +} + +function ViewSettings({ setCardsPerRow }: { setCardsPerRow: (cardsPerRow?: string) => void }) { + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); + + return ( + setIsPopoverOpen(!isPopoverOpen)} + /> + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + anchorPosition="downCenter" + > + + + +
    + +
    +
    + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts new file mode 100644 index 0000000000000..169e1e54c5222 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.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 { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo'; + +export function useSloListActions({ + slo, + setIsAddRuleFlyoutOpen, + setIsActionsPopoverOpen, + setDeleteConfirmationModalOpen, +}: { + slo: SLOWithSummaryResponse; + setIsActionsPopoverOpen: (val: boolean) => void; + setIsAddRuleFlyoutOpen: (val: boolean) => void; + setDeleteConfirmationModalOpen: (val: boolean) => void; +}) { + const { mutate: deleteSlo } = useDeleteSlo(); + + const handleDeleteConfirm = () => { + setDeleteConfirmationModalOpen(false); + deleteSlo({ id: slo.id, name: slo.name }); + }; + + const handleDeleteCancel = () => { + setDeleteConfirmationModalOpen(false); + }; + const handleCreateRule = () => { + setIsActionsPopoverOpen(false); + setIsAddRuleFlyoutOpen(true); + }; + + return { + handleDeleteConfirm, + handleDeleteCancel, + handleCreateRule, + }; +} diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_summary.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_summary.ts new file mode 100644 index 0000000000000..547bd41b4f5db --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_summary.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 numeral from '@elastic/numeral'; +import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { paths } from '../../../../common/locators/paths'; +import { useKibana } from '../../../utils/kibana_react'; +import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; + +export const useSloFormattedSummary = (slo: SLOWithSummaryResponse) => { + const { + http: { basePath }, + } = useKibana().services; + const { uiSettings } = useKibana().services; + const percentFormat = uiSettings.get('format:percent:defaultPattern'); + + const sliValue = + slo.summary.status === 'NO_DATA' + ? NOT_AVAILABLE_LABEL + : numeral(slo.summary.sliValue).format(percentFormat); + + const sloTarget = numeral(slo.objective.target).format(percentFormat); + const errorBudgetRemaining = + slo.summary.errorBudget.remaining <= 0 + ? Math.trunc(slo.summary.errorBudget.remaining * 100) / 100 + : slo.summary.errorBudget.remaining; + + const errorBudgetRemainingTitle = + slo.summary.status === 'NO_DATA' + ? NOT_AVAILABLE_LABEL + : numeral(errorBudgetRemaining).format(percentFormat); + + const sloDetailsUrl = basePath.prepend( + paths.observability.sloDetails( + slo.id, + slo.groupBy !== ALL_VALUE && slo.instanceId ? slo.instanceId : undefined + ) + ); + + return { + sloDetailsUrl, + sliValue, + sloTarget, + errorBudgetRemaining: errorBudgetRemainingTitle, + }; +}; diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts index 7a0c03215fb91..aaa87cb921ae6 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts @@ -8,6 +8,7 @@ import { useHistory } from 'react-router-dom'; import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import deepmerge from 'deepmerge'; +import { ViewMode } from '../components/badges/slo_badges'; import type { SortField } from '../components/slo_list_search_bar'; export const SLO_LIST_SEARCH_URL_STORAGE_KEY = 'search'; @@ -19,12 +20,14 @@ export interface SearchState { by: SortField; direction: 'asc' | 'desc'; }; + viewMode: ViewMode; } export const DEFAULT_STATE = { kqlQuery: '', page: 0, sort: { by: 'status' as const, direction: 'desc' as const }, + viewMode: 'compact' as const, }; export function useUrlSearchState(): { diff --git a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx index 3e19b7a466be5..6c63d270ef8e8 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { act, screen, waitFor } from '@testing-library/react'; +import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; @@ -211,6 +211,9 @@ describe('SLOs Page', () => { await act(async () => { render(); }); + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + + fireEvent.click(screen.getByTestId('sloListViewButton')); expect(screen.queryByTestId('slosPage')).toBeTruthy(); expect(screen.queryByTestId('sloList')).toBeTruthy(); @@ -229,6 +232,8 @@ describe('SLOs Page', () => { await act(async () => { render(); }); + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + fireEvent.click(screen.getByTestId('sloListViewButton')); screen.getAllByLabelText('Actions').at(0)?.click(); @@ -256,7 +261,8 @@ describe('SLOs Page', () => { await act(async () => { render(); }); - + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + fireEvent.click(screen.getByTestId('sloListViewButton')); screen.getAllByLabelText('Actions').at(0)?.click(); await waitForEuiPopoverOpen(); @@ -281,7 +287,8 @@ describe('SLOs Page', () => { await act(async () => { render(); }); - + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + fireEvent.click(screen.getByTestId('sloListViewButton')); screen.getAllByLabelText('Actions').at(0)?.click(); await waitForEuiPopoverOpen(); @@ -307,6 +314,8 @@ describe('SLOs Page', () => { render(); }); + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + fireEvent.click(screen.getByTestId('sloListViewButton')); screen.getAllByLabelText('Actions').at(0)?.click(); await waitForEuiPopoverOpen(); @@ -337,6 +346,8 @@ describe('SLOs Page', () => { render(); }); + expect(await screen.findByTestId('sloListViewButton')).toBeTruthy(); + fireEvent.click(screen.getByTestId('sloListViewButton')); screen.getAllByLabelText('Actions').at(0)?.click(); await waitForEuiPopoverOpen(); diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index 579b1bb8d19ae..e010df224ce1c 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -32,7 +32,7 @@ export function SlosPage() { const { hasWriteCapabilities } = useCapabilities(); const { hasAtLeast } = useLicense(); - const { isInitialLoading, isLoading, isError, data: sloList } = useFetchSloList(); + const { isLoading, isError, data: sloList } = useFetchSloList(); const { total } = sloList ?? { total: 0 }; const { storeAutoRefreshState, getAutoRefreshState } = useAutoRefreshStorage(); @@ -63,10 +63,6 @@ export function SlosPage() { storeAutoRefreshState(!isAutoRefreshing); }; - if (isInitialLoading) { - return null; - } - return ( { - const { SloOverviewEmbeddableFactoryDefinition } = await import( - './embeddable/slo/overview/slo_embeddable_factory' - ); - const factory = new SloOverviewEmbeddableFactoryDefinition(coreSetup.getStartServices); - pluginsSetup.embeddable.registerEmbeddableFactory(factory.type, factory); + + const assertPlatinumLicense = async () => { + const licensing = await pluginsSetup.licensing; + const license = await firstValueFrom(licensing.license$); + + const hasPlatinumLicense = license.hasAtLeast('platinum'); + if (hasPlatinumLicense) { + const registerSloOverviewEmbeddableFactory = async () => { + const { SloOverviewEmbeddableFactoryDefinition } = await import( + './embeddable/slo/overview/slo_embeddable_factory' + ); + const factory = new SloOverviewEmbeddableFactoryDefinition(coreSetup.getStartServices); + pluginsSetup.embeddable.registerEmbeddableFactory(factory.type, factory); + }; + registerSloOverviewEmbeddableFactory(); + } }; - registerSloEmbeddableFactory(); + + assertPlatinumLicense(); if (pluginsSetup.home) { pluginsSetup.home.featureCatalogue.registerSolution({ diff --git a/x-pack/plugins/observability/public/routes/routes.tsx b/x-pack/plugins/observability/public/routes/routes.tsx index e1559050056f0..87fc22c094344 100644 --- a/x-pack/plugins/observability/public/routes/routes.tsx +++ b/x-pack/plugins/observability/public/routes/routes.tsx @@ -37,6 +37,7 @@ import { SLO_DETAIL_PATH, SLO_EDIT_PATH, } from '../../common/locators/paths'; +import { HasDataContextProvider } from '../context/has_data_context/has_data_context'; // Note: React Router DOM component was not working here // so I've recreated this simple version for this purpose. @@ -65,7 +66,11 @@ export const routes = { }, [LANDING_PATH]: { handler: () => { - return ; + return ( + + + + ); }, params: {}, exact: true, @@ -73,9 +78,11 @@ export const routes = { [OVERVIEW_PATH]: { handler: () => { return ( - - - + + + + + ); }, params: {}, diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts index e5301b8ac8da1..c2340fbc046fb 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts @@ -21,7 +21,6 @@ import { CustomThresholdAlertContext } from './types'; import { Evaluation } from './lib/evaluate_rule'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; -import { CUSTOM_AGGREGATOR } from '../../../../common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -1883,7 +1882,6 @@ declare global { } const customThresholdNonCountCriterion: CustomMetricExpressionParams = { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { @@ -1898,7 +1896,6 @@ const customThresholdNonCountCriterion: CustomMetricExpressionParams = { }; const customThresholdCountCriterion: CustomMetricExpressionParams = { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, metrics: [ { diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts index ad4aaa980aa63..2e5eda9fa32b4 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts @@ -19,7 +19,8 @@ export const createConditionScript = (threshold: number[], comparator: Comparato } if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { return { - source: `params.value < params.threshold0 && params.value > params.threshold1 ? 1 : 0`, + // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], threshold1: threshold[1], diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts index 53a89303fa171..01d94dd5f7d67 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts @@ -6,7 +6,6 @@ */ import moment from 'moment'; -import { CUSTOM_AGGREGATOR } from '../../../../../common/custom_threshold_rule/constants'; import { Comparator, Aggregators, @@ -23,7 +22,6 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { field: 'system.is.a.good.puppy.dog', }, ], - aggType: CUSTOM_AGGREGATOR, timeUnit: 'm', timeSize: 1, threshold: [1], diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index 2166c21db8609..db4402dca7e88 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -43,7 +43,6 @@ import { oneOfLiterals, validateKQLStringFilter } from './utils'; import { createCustomThresholdExecutor } from './custom_threshold_executor'; import { FIRED_ACTION, NO_DATA_ACTION } from './constants'; import { ObservabilityConfig } from '../../..'; -import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/custom_threshold_rule/constants'; export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts = { context: THRESHOLD_RULE_REGISTRATION_CONTEXT, @@ -79,27 +78,9 @@ export function thresholdRuleType( timeSize: schema.number(), }; - const nonCountCriterion = schema.object({ - ...baseCriterion, - metric: schema.string(), - aggType: oneOfLiterals(METRIC_EXPLORER_AGGREGATIONS), - metrics: schema.never(), - equation: schema.never(), - label: schema.never(), - }); - - const countCriterion = schema.object({ - ...baseCriterion, - aggType: schema.literal('count'), - metric: schema.never(), - metrics: schema.never(), - equation: schema.never(), - label: schema.never(), - }); - const customCriterion = schema.object({ ...baseCriterion, - aggType: schema.literal('custom'), + aggType: schema.maybe(schema.literal('custom')), metric: schema.never(), metrics: schema.arrayOf( schema.oneOf([ @@ -133,9 +114,7 @@ export function thresholdRuleType( validate: { params: schema.object( { - criteria: schema.arrayOf( - schema.oneOf([countCriterion, nonCountCriterion, customCriterion]) - ), + criteria: schema.arrayOf(customCriterion), groupBy: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), alertOnNoData: schema.maybe(schema.boolean()), alertOnGroupDisappear: schema.maybe(schema.boolean()), diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 17476d335f48f..d1ca0e45b74a2 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -175,6 +175,36 @@ export class ObservabilityPlugin implements Plugin { }, ], }, + { + name: i18n.translate('xpack.observability.featureRegistry.casesSettingsSubFeatureName', { + defaultMessage: 'Case Settings', + }), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cases_settings', + name: i18n.translate( + 'xpack.observability.featureRegistry.casesSettingsSubFeatureDetails', + { + defaultMessage: 'Edit Case Settings', + } + ), + includeIn: 'all', + savedObject: { + all: [...filesSavedObjectTypes], + read: [...filesSavedObjectTypes], + }, + cases: { + settings: [observabilityFeatureId], + }, + ui: casesCapabilities.settings, + }, + ], + }, + ], + }, ], }); diff --git a/x-pack/plugins/observability_ai_assistant/jest.config.js b/x-pack/plugins/observability_ai_assistant/jest.config.js index 5eaabe2dcf492..1d6798f6c7623 100644 --- a/x-pack/plugins/observability_ai_assistant/jest.config.js +++ b/x-pack/plugins/observability_ai_assistant/jest.config.js @@ -10,4 +10,6 @@ module.exports = { rootDir: '../../..', roots: ['/x-pack/plugins/observability_ai_assistant'], setupFiles: ['/x-pack/plugins/observability_ai_assistant/.storybook/jest_setup.js'], + collectCoverage: true, + coverageReporters: ['html'], }; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/action_menu_item/action_menu_item.tsx b/x-pack/plugins/observability_ai_assistant/public/components/action_menu_item/action_menu_item.tsx index ca47c242df495..e77b055e568fa 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/action_menu_item/action_menu_item.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/action_menu_item/action_menu_item.tsx @@ -6,19 +6,15 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiHeaderLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { ObservabilityAIAssistantChatServiceProvider } from '../../context/observability_ai_assistant_chat_service_provider'; import { useAbortableAsync } from '../../hooks/use_abortable_async'; -import { useConversation } from '../../hooks/use_conversation'; -import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; -import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; import { AssistantAvatar } from '../assistant_avatar'; import { ChatFlyout } from '../chat/chat_flyout'; export function ObservabilityAIAssistantActionMenuItem() { const service = useObservabilityAIAssistant(); - const connectors = useGenAIConnectors(); const [isOpen, setIsOpen] = useState(false); @@ -32,14 +28,7 @@ export function ObservabilityAIAssistantActionMenuItem() { [service, isOpen] ); - const [conversationId, setConversationId] = useState(); - - const { conversation, displayedMessages, setDisplayedMessages, save, saveTitle } = - useConversation({ - conversationId, - connectorId: connectors.selectedConnector, - chatService: chatService.value, - }); + const initialMessages = useMemo(() => [], []); if (!service.isEnabled()) { return null; @@ -72,26 +61,12 @@ export function ObservabilityAIAssistantActionMenuItem() { {chatService.value ? ( { - setIsOpen(() => false); - }} - onChatComplete={(messages) => { - save(messages) - .then((nextConversation) => { - setConversationId(nextConversation.conversation.id); - }) - .catch(() => {}); - }} - onChatUpdate={(nextMessages) => { - setDisplayedMessages(nextMessages); - }} - onChatTitleSave={(newTitle) => { - saveTitle(newTitle); + setIsOpen(false); }} /> diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx index 4b8d749abf9f4..52c927795c416 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.stories.tsx @@ -21,8 +21,8 @@ const meta: ComponentMeta = { export default meta; const defaultProps: ComponentStoryObj = { args: { - title: 'My Conversation', - messages: [ + initialTitle: 'My Conversation', + initialMessages: [ getAssistantSetupMessage({ contexts: [] }), { '@timestamp': new Date().toISOString(), @@ -64,8 +64,6 @@ const defaultProps: ComponentStoryObj = { currentUser: { username: 'elastic', }, - onChatUpdate: () => {}, - onChatComplete: () => {}, }, render: (props) => { return ( diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx index 5e4aa5e0659eb..afbb042d2eadc 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import React, { useEffect, useRef, useState } from 'react'; -import { flatten, last } from 'lodash'; import { + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -16,21 +15,26 @@ import { EuiSpacer, } from '@elastic/eui'; import { css } from '@emotion/css'; -import { euiThemeVars } from '@kbn/ui-theme'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; -import type { Message } from '../../../common/types'; +import { euiThemeVars } from '@kbn/ui-theme'; +import React, { useEffect, useRef, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { Conversation, Message, MessageRole } from '../../../common/types'; +import { ChatState } from '../../hooks/use_chat'; +import { useConversation } from '../../hooks/use_conversation'; import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; -import { useTimeline } from '../../hooks/use_timeline'; import { useLicense } from '../../hooks/use_license'; import { useObservabilityAIAssistantChatService } from '../../hooks/use_observability_ai_assistant_chat_service'; -import { ExperimentalFeatureBanner } from './experimental_feature_banner'; -import { InitialSetupPanel } from './initial_setup_panel'; -import { IncorrectLicensePanel } from './incorrect_license_panel'; +import { StartedFrom } from '../../utils/get_timeline_items_from_conversation'; import { ChatHeader } from './chat_header'; import { ChatPromptEditor } from './chat_prompt_editor'; import { ChatTimeline } from './chat_timeline'; -import { StartedFrom } from '../../utils/get_timeline_items_from_conversation'; +import { ExperimentalFeatureBanner } from './experimental_feature_banner'; +import { IncorrectLicensePanel } from './incorrect_license_panel'; +import { InitialSetupPanel } from './initial_setup_panel'; +import { ChatActionClickType } from './types'; +import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; const timelineClassName = css` overflow-y: auto; @@ -45,48 +49,45 @@ const incorrectLicenseContainer = css` padding: ${euiThemeVars.euiPanelPaddingModifiers.paddingMedium}; `; +const chatBodyContainerClassNameWithError = css` + align-self: center; +`; + export function ChatBody({ - title, - loading, - messages, + initialTitle, + initialMessages, + initialConversationId, connectors, knowledgeBase, connectorsManagementHref, modelsManagementHref, - conversationId, currentUser, startedFrom, - onChatUpdate, - onChatComplete, - onSaveTitle, + onConversationUpdate, }: { - title: string; - loading: boolean; - messages: Message[]; + initialTitle?: string; + initialMessages?: Message[]; + initialConversationId?: string; connectors: UseGenAIConnectorsResult; knowledgeBase: UseKnowledgeBaseResult; connectorsManagementHref: string; modelsManagementHref: string; - conversationId?: string; currentUser?: Pick; startedFrom?: StartedFrom; - onChatUpdate: (messages: Message[]) => void; - onChatComplete: (messages: Message[]) => void; - onSaveTitle: (title: string) => void; + onConversationUpdate: (conversation: Conversation) => void; }) { const license = useLicense(); const hasCorrectLicense = license?.hasAtLeast('enterprise'); const chatService = useObservabilityAIAssistantChatService(); - const timeline = useTimeline({ + const { conversation, messages, next, state, stop, saveTitle } = useConversation({ + initialConversationId, + initialMessages, + initialTitle, chatService, - connectors, - currentUser, - messages, - startedFrom, - onChatUpdate, - onChatComplete, + connectorId: connectors.selectedConnector, + onConversationUpdate, }); const timelineContainerRef = useRef(null); @@ -94,7 +95,10 @@ export function ChatBody({ let footer: React.ReactNode; const isLoading = Boolean( - connectors.loading || knowledgeBase.status.loading || last(flatten(timeline.items))?.loading + connectors.loading || + knowledgeBase.status.loading || + state === ChatState.Loading || + conversation.loading ); const containerClassName = css` @@ -139,12 +143,12 @@ export function ChatBody({ }); const handleCopyConversation = () => { - const content = JSON.stringify({ title, messages }); + const content = JSON.stringify({ title: initialTitle, messages }); navigator.clipboard?.writeText(content || ''); }; - if (!hasCorrectLicense && !conversationId) { + if (!hasCorrectLicense && !initialConversationId) { footer = ( <> @@ -155,19 +159,29 @@ export function ChatBody({ - + { + next(messages.concat(message)); + }} + /> ); - } else if (connectors.loading || knowledgeBase.status.loading) { + } else if ( + connectors.loading || + knowledgeBase.status.loading || + (!conversation.value && conversation.loading) + ) { footer = ( ); - } else if (connectors.connectors?.length === 0 && !conversationId) { + } else if (connectors.connectors?.length === 0 && !initialConversationId) { footer = ( { + const indexOf = messages.indexOf(editedMessage); + next(messages.slice(0, indexOf).concat(newMessage)); + }} + onFeedback={(message, feedback) => {}} + onRegenerate={(message) => { + const indexOf = messages.indexOf(message); + next(messages.slice(0, indexOf)); + }} + onStopGenerating={() => { + stop(); + }} onActionClick={(payload) => { setStickToBottom(true); - return timeline.onActionClick(payload); + switch (payload.type) { + case ChatActionClickType.executeEsqlQuery: + next( + messages.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'execute_query', + arguments: JSON.stringify({ + query: payload.query, + }), + trigger: MessageRole.User, + }, + }, + }) + ); + break; + } }} /> @@ -207,7 +253,7 @@ export function ChatBody({ disabled={!connectors.selectedConnector || !hasCorrectLicense} onSubmit={(message) => { setStickToBottom(true); - return timeline.onSubmit(message); + return next(messages.concat(message)); }} /> @@ -217,6 +263,33 @@ export function ChatBody({ ); } + if (conversation.error) { + return ( + + + + {i18n.translate('xpack.observabilityAiAssistant.couldNotFindConversationContent', { + defaultMessage: + 'Could not find a conversation with id {conversationId}. Make sure the conversation exists and you have access to it.', + values: { conversationId: initialConversationId }, + })} + + + + ); + } + return ( {connectors.selectedConnector ? ( @@ -224,20 +297,45 @@ export function ChatBody({
    ) : null} - + + {conversation.error ? ( + + {i18n.translate('xpack.observabilityAiAssistant.couldNotFindConversationContent', { + defaultMessage: + 'Could not find a conversation with id {conversationId}. Make sure the conversation exists and you have access to it.', + values: { conversationId: initialConversationId }, + })} + + ) : null} + { + saveTitle(newTitle); + }} /> diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_consolidated_items.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_consolidated_items.tsx index 346ccfe501f37..18a98c09ff387 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_consolidated_items.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_consolidated_items.tsx @@ -120,12 +120,12 @@ export function ChatConsolidatedItems({ key={index} {...item} onFeedbackClick={(feedback) => { - onFeedback(item, feedback); + onFeedback(item.message, feedback); }} onRegenerateClick={() => { - onRegenerate(item); + onRegenerate(item.message); }} - onEditSubmit={(message) => onEditSubmit(item, message)} + onEditSubmit={(message) => onEditSubmit(item.message, message)} onStopGeneratingClick={onStopGenerating} onActionClick={onActionClick} /> diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx index 30f56a6ab63a0..bf54e20c3ec50 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.stories.tsx @@ -29,13 +29,10 @@ const Template: ComponentStory = (props: ChatFlyoutProps) => { const defaultProps: ChatFlyoutProps = { isOpen: true, - title: 'How is this working', - messages: [getAssistantSetupMessage({ contexts: [] })], + initialTitle: 'How is this working', + initialMessages: [getAssistantSetupMessage({ contexts: [] })], startedFrom: 'appTopNavbar', onClose: () => {}, - onChatComplete: () => {}, - onChatTitleSave: () => {}, - onChatUpdate: () => {}, }; export const ChatFlyout = Template.bind({}); diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx index ef4635d873e8a..ef4961b87b7c6 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_flyout.tsx @@ -7,7 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFlyout, EuiLink, EuiPanel, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/css'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useState } from 'react'; import type { Message } from '../../../common/types'; import { useCurrentUser } from '../../hooks/use_current_user'; import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; @@ -28,25 +28,17 @@ const bodyClassName = css` `; export function ChatFlyout({ - title, - messages, - conversationId, + initialTitle, + initialMessages, + onClose, isOpen, startedFrom, - onClose, - onChatUpdate, - onChatComplete, - onChatTitleSave, }: { - title: string; - messages: Message[]; - conversationId?: string; + initialTitle: string; + initialMessages: Message[]; isOpen: boolean; startedFrom: StartedFrom; onClose: () => void; - onChatUpdate: (messages: Message[]) => void; - onChatComplete: (messages: Message[]) => void; - onChatTitleSave: (title: string) => void; }) { const { euiTheme } = useEuiTheme(); const { @@ -61,6 +53,8 @@ export function ChatFlyout({ const knowledgeBase = useKnowledgeBase(); + const [conversationId, setConversationId] = useState(undefined); + return isOpen ? ( { - if (onChatUpdate) { - onChatUpdate(nextMessages); - } - }} - onChatComplete={(nextMessages) => { - if (onChatComplete) { - onChatComplete(nextMessages); - } - }} - onSaveTitle={(newTitle) => { - onChatTitleSave(newTitle); + onConversationUpdate={(conversation) => { + setConversationId(conversation.conversation.id); }} /> diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx index 0630f9c36d9dd..977b342bbe1d7 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_header.tsx @@ -90,7 +90,12 @@ export function ChatHeader({ { defaultMessage: 'Edit conversation' } )} editModeProps={{ inputProps: { inputRef } }} - isReadOnly={!connectors.selectedConnector || licenseInvalid || !Boolean(onSaveTitle)} + isReadOnly={ + !conversationId || + !connectors.selectedConnector || + licenseInvalid || + !Boolean(onSaveTitle) + } onSave={onSaveTitle} /> ) : null} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx index 7ec1084a26b22..09cb52a1c30b9 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item.tsx @@ -27,7 +27,7 @@ import { FailedToLoadResponse } from '../message_panel/failed_to_load_response'; import { ChatActionClickHandler } from './types'; export interface ChatItemProps extends ChatTimelineItem { - onEditSubmit: (message: Message) => Promise; + onEditSubmit: (message: Message) => void; onFeedbackClick: (feedback: Feedback) => void; onRegenerateClick: () => void; onStopGeneratingClick: () => void; @@ -66,13 +66,14 @@ const noPanelMessageClassName = css` export function ChatItem({ actions: { canCopy, canEdit, canGiveFeedback, canRegenerate }, display: { collapsed }, + message: { + message: { function_call: functionCall, role }, + }, content, currentUser, element, error, - function_call: functionCall, loading, - role, title, onEditSubmit, onFeedbackClick, diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_content_inline_prompt_editor.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_content_inline_prompt_editor.tsx index df57f069d91d1..4f702eed2e16d 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_content_inline_prompt_editor.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_item_content_inline_prompt_editor.tsx @@ -22,7 +22,7 @@ interface Props { | undefined; loading: boolean; editing: boolean; - onSubmit: (message: Message) => Promise; + onSubmit: (message: Message) => void; onActionClick: ChatActionClickHandler; } export function ChatItemContentInlinePromptEditor({ diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx index 21e9e3871205c..f34288d5755e6 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor.tsx @@ -5,20 +5,20 @@ * 2.0. */ -import React, { useCallback, useEffect, useRef, useState } from 'react'; import { EuiButtonEmpty, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiFocusTrap, EuiPanel, EuiSpacer, EuiTextArea, keys, - EuiFocusTrap, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { MessageRole, type Message } from '../../../common'; import { useJsonEditorModel } from '../../hooks/use_json_editor_model'; import { FunctionListPopover } from './function_list_popover'; @@ -30,7 +30,7 @@ export interface ChatPromptEditorProps { initialSelectedFunctionName?: string; initialFunctionPayload?: string; trigger?: MessageRole; - onSubmit: (message: Message) => Promise; + onSubmit: (message: Message) => void; } export function ChatPromptEditor({ @@ -216,7 +216,10 @@ export function ChatPromptEditor({ {selectedFunctionName ? ( 8 ? '200px' : '120px'} @@ -284,7 +287,10 @@ export function ChatPromptEditor({ = (props: ChatTimelineProps) => { - const [count, setCount] = useState(props.items.length - 1); + const [count, setCount] = useState(props.messages.length - 1); return ( <> - index <= count)} /> + index <= count)} /> setCount(count >= 0 && count < props.items.length - 1 ? count + 1 : 0)} + onClick={() => setCount(count >= 0 && count < props.messages.length - 1 ? count + 1 : 0)} > Add message @@ -61,13 +63,23 @@ const defaultProps: ComponentProps = { installError: undefined, install: async () => {}, }, - items: [ - buildChatInitItem(), - buildUserChatItem(), - buildAssistantChatItem(), - buildUserChatItem({ content: 'How does it work?' }), - buildAssistantChatItem({ - content: `The way functions work depends on whether we are talking about mathematical functions or programming functions. Let's explore both: + chatService: { + hasRenderFunction: () => false, + } as unknown as ObservabilityAIAssistantChatService, + chatState: ChatState.Ready, + hasConnector: true, + currentUser: { + full_name: 'John Doe', + username: 'johndoe', + }, + messages: [ + buildSystemMessage(), + buildUserMessage(), + buildAssistantMessage(), + buildUserMessage({ message: { content: 'How does it work?' } }), + buildAssistantMessage({ + message: { + content: `The way functions work depends on whether we are talking about mathematical functions or programming functions. Let's explore both: Mathematical Functions: In mathematics, a function maps input values to corresponding output values based on a specific rule or expression. The general process of how a mathematical function works can be summarized as follows: @@ -78,55 +90,34 @@ const defaultProps: ComponentProps = { Step 3: Output - After processing the input, the function produces an output value, denoted as 'f(x)' or 'y'. This output represents the dependent variable and is the result of applying the function's rule to the input. Step 4: Uniqueness - A well-defined mathematical function ensures that each input value corresponds to exactly one output value. In other words, the function should yield the same output for the same input whenever it is called.`, + }, }), - buildUserChatItem({ - content: 'Can you execute a function?', + buildUserMessage({ + message: { content: 'Can you execute a function?' }, }), - buildAssistantChatItem({ - content: 'Sure, I can do that.', - title: 'suggested a function', - function_call: { - name: 'a_function', - arguments: '{ "foo": "bar" }', - trigger: MessageRole.Assistant, - }, - actions: { - canEdit: false, - canCopy: true, - canGiveFeedback: true, - canRegenerate: true, + buildAssistantMessage({ + message: { + content: 'Sure, I can do that.', + function_call: { + name: 'a_function', + arguments: '{ "foo": "bar" }', + trigger: MessageRole.Assistant, + }, }, }), - buildFunctionChatItem({ - content: '{ "message": "The arguments are wrong" }', - error: new Error(), - actions: { - canRegenerate: false, - canEdit: true, - canGiveFeedback: false, - canCopy: true, - }, + buildFunctionResponseMessage({ + message: { content: '{ "message": "The arguments are wrong" }' }, }), - buildAssistantChatItem({ - content: '', - title: 'suggested a function', - function_call: { - name: 'a_function', - arguments: '{ "bar": "foo" }', - trigger: MessageRole.Assistant, - }, - actions: { - canEdit: true, - canCopy: true, - canGiveFeedback: true, - canRegenerate: true, + buildAssistantMessage({ + message: { + content: '', + function_call: { + name: 'a_function', + arguments: '{ "bar": "foo" }', + trigger: MessageRole.Assistant, + }, }, }), - buildFunctionChatItem({ - content: '', - title: 'are executing a function', - loading: true, - }), ], onEdit: async () => {}, onFeedback: () => {}, diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx index e42924e765609..065208695349d 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_timeline.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { css } from '@emotion/css'; import { EuiCommentList } from '@elastic/eui'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; @@ -16,6 +16,12 @@ import type { Feedback } from '../feedback_buttons'; import { type Message } from '../../../common'; import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; import type { ChatActionClickHandler } from './types'; +import { + getTimelineItemsfromConversation, + StartedFrom, +} from '../../utils/get_timeline_items_from_conversation'; +import { ObservabilityAIAssistantChatService } from '../../types'; +import { ChatState } from '../../hooks/use_chat'; export interface ChatTimelineItem extends Pick { @@ -35,27 +41,70 @@ export interface ChatTimelineItem element?: React.ReactNode; currentUser?: Pick; error?: any; + message: Message; } export interface ChatTimelineProps { - items: Array; + messages: Message[]; knowledgeBase: UseKnowledgeBaseResult; - onEdit: (item: ChatTimelineItem, message: Message) => Promise; - onFeedback: (item: ChatTimelineItem, feedback: Feedback) => void; - onRegenerate: (item: ChatTimelineItem) => void; + chatService: ObservabilityAIAssistantChatService; + hasConnector: boolean; + chatState: ChatState; + currentUser?: Pick; + startedFrom?: StartedFrom; + onEdit: (message: Message, messageAfterEdit: Message) => void; + onFeedback: (message: Message, feedback: Feedback) => void; + onRegenerate: (message: Message) => void; onStopGenerating: () => void; onActionClick: ChatActionClickHandler; } export function ChatTimeline({ - items = [], + messages, knowledgeBase, + chatService, + hasConnector, + currentUser, + startedFrom, onEdit, onFeedback, onRegenerate, onStopGenerating, onActionClick, + chatState, }: ChatTimelineProps) { + const items = useMemo(() => { + const timelineItems = getTimelineItemsfromConversation({ + chatService, + hasConnector, + messages, + currentUser, + startedFrom, + chatState, + }); + + const consolidatedChatItems: Array = []; + let currentGroup: ChatTimelineItem[] | null = null; + + for (const item of timelineItems) { + if (item.display.hide || !item) continue; + + if (item.display.collapsed) { + if (currentGroup) { + currentGroup.push(item); + } else { + currentGroup = [item]; + consolidatedChatItems.push(currentGroup); + } + } else { + consolidatedChatItems.push(item); + currentGroup = null; + } + } + + return consolidatedChatItems; + }, [chatService, hasConnector, messages, currentUser, startedFrom, chatState]); + return ( ) : ( - items.map((item, index) => - Array.isArray(item) ? ( + items.map((item, index) => { + return Array.isArray(item) ? ( { - onFeedback(item, feedback); + onFeedback(item.message, feedback); }} onRegenerateClick={() => { - onRegenerate(item); + onRegenerate(item.message); + }} + onEditSubmit={(message) => { + onEdit(item.message, message); }} - onEditSubmit={(message) => onEdit(item, message)} onStopGeneratingClick={onStopGenerating} onActionClick={onActionClick} /> - ) - ) + ); + }) )} ); diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/types.ts b/x-pack/plugins/observability_ai_assistant/public/components/chat/types.ts index 4edd3d7dcdda0..017f2f81a6f63 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/types.ts +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/types.ts @@ -20,4 +20,4 @@ export enum ChatActionClickType { executeEsqlQuery = 'executeEsqlQuery', } -export type ChatActionClickHandler = (payload: ChatActionClickPayload) => Promise; +export type ChatActionClickHandler = (payload: ChatActionClickPayload) => void; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx index 8c6463ca9318c..48ba86a98fbe8 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx @@ -4,20 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { first } from 'lodash'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { AbortError } from '@kbn/kibana-utils-plugin/common'; -import { isObservable, Subscription } from 'rxjs'; +import { last } from 'lodash'; +import React, { useEffect, useRef, useState } from 'react'; import { MessageRole, type Message } from '../../../common/types'; import { ObservabilityAIAssistantChatServiceProvider } from '../../context/observability_ai_assistant_chat_service_provider'; -import { useKibana } from '../../hooks/use_kibana'; import { useAbortableAsync } from '../../hooks/use_abortable_async'; -import { useConversation } from '../../hooks/use_conversation'; +import { ChatState, useChat } from '../../hooks/use_chat'; import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; +import { useKibana } from '../../hooks/use_kibana'; import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; import { useObservabilityAIAssistantChatService } from '../../hooks/use_observability_ai_assistant_chat_service'; -import type { PendingMessage } from '../../types'; import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href'; import { RegenerateResponseButton } from '../buttons/regenerate_response_button'; import { StartChatButton } from '../buttons/start_chat_button'; @@ -40,197 +37,40 @@ function ChatContent({ }) { const chatService = useObservabilityAIAssistantChatService(); - const [pendingMessage, setPendingMessage] = useState(); - - const [loading, setLoading] = useState(false); - const [subscription, setSubscription] = useState(); + const initialMessagesRef = useRef(initialMessages); - const [conversationId, setConversationId] = useState(); - - const { - conversation, - displayedMessages, - setDisplayedMessages, - getSystemMessage, - save, - saveTitle, - } = useConversation({ - conversationId, - connectorId, + const { messages, next, state, stop } = useChat({ chatService, + connectorId, initialMessages, }); - const conversationTitle = conversationId - ? conversation.value?.conversation.title || '' - : defaultTitle; - - const controllerRef = useRef(new AbortController()); - - const reloadRecalledMessages = useCallback( - async (messages: Message[]) => { - controllerRef.current.abort(); - - const controller = (controllerRef.current = new AbortController()); - - const isStartOfConversation = - messages.some((message) => message.message.role === MessageRole.Assistant) === false; - - if (isStartOfConversation && chatService.hasFunction('recall')) { - // manually execute recall function and append to list of - // messages - const functionCall = { - name: 'recall', - args: JSON.stringify({ queries: [], contexts: [] }), - }; - - const response = await chatService.executeFunction({ - ...functionCall, - messages, - signal: controller.signal, - connectorId, - }); - - if (isObservable(response)) { - throw new Error('Recall function unexpectedly returned an Observable'); - } - - return [ - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: functionCall.name, - arguments: functionCall.args, - trigger: MessageRole.User as const, - }, - }, - }, - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.User, - name: functionCall.name, - content: JSON.stringify(response.content), - }, - }, - ]; - } - - return []; - }, - [chatService, connectorId] + const lastAssistantResponse = last( + messages.filter((message) => message.message.role === MessageRole.Assistant) ); - const reloadConversation = useCallback(async () => { - setLoading(true); - - setDisplayedMessages(initialMessages); - setPendingMessage(undefined); - - const messages = [getSystemMessage(), ...initialMessages]; - - const recalledMessages = await reloadRecalledMessages(messages); - const next = messages.concat(recalledMessages); - - setDisplayedMessages(next); - - let lastPendingMessage: PendingMessage | undefined; - - const nextSubscription = chatService - .chat({ messages: next, connectorId, function: 'none' }) - .subscribe({ - next: (msg) => { - lastPendingMessage = msg; - setPendingMessage(() => msg); - }, - complete: () => { - setDisplayedMessages((prev) => - prev.concat({ - '@timestamp': new Date().toISOString(), - ...lastPendingMessage!, - }) - ); - setPendingMessage(lastPendingMessage); - setLoading(false); - }, - }); - - setSubscription(nextSubscription); - }, [ - reloadRecalledMessages, - chatService, - connectorId, - initialMessages, - getSystemMessage, - setDisplayedMessages, - ]); - - useEffect(() => { - reloadConversation(); - }, [reloadConversation]); - useEffect(() => { - setDisplayedMessages(initialMessages); - }, [initialMessages, setDisplayedMessages]); + next(initialMessagesRef.current); + }, [next]); const [isOpen, setIsOpen] = useState(false); - const messagesWithPending = useMemo(() => { - return pendingMessage - ? displayedMessages.concat({ - '@timestamp': new Date().toISOString(), - message: { - ...pendingMessage.message, - }, - }) - : displayedMessages; - }, [pendingMessage, displayedMessages]); - - const firstAssistantMessage = first( - messagesWithPending.filter( - (message) => - message.message.role === MessageRole.Assistant && - (!message.message.function_call?.trigger || - message.message.function_call.trigger === MessageRole.Assistant) - ) - ); - return ( <> {}} /> } - error={pendingMessage?.error} + error={state === ChatState.Error} controls={ - loading ? ( + state === ChatState.Loading ? ( { - subscription?.unsubscribe(); - setLoading(false); - setDisplayedMessages((prevMessages) => - prevMessages.concat({ - '@timestamp': new Date().toISOString(), - message: { - ...pendingMessage!.message, - }, - }) - ); - setPendingMessage((prev) => ({ - message: { - role: MessageRole.Assistant, - ...prev?.message, - }, - aborted: true, - error: new AbortError(), - })); + stop(); }} /> ) : ( @@ -238,7 +78,7 @@ function ChatContent({ { - reloadConversation(); + next(initialMessages); }} /> @@ -254,33 +94,27 @@ function ChatContent({ } /> { - setIsOpen(() => false); + setIsOpen(false); }} - messages={displayedMessages} - conversationId={conversationId} + initialMessages={messages} + initialTitle={defaultTitle} startedFrom="contextualInsight" - onChatComplete={(nextMessages) => { - save(nextMessages) - .then((nextConversation) => { - setConversationId(nextConversation.conversation.id); - }) - .catch(() => {}); - }} - onChatUpdate={(nextMessages) => { - setDisplayedMessages(nextMessages); - }} - onChatTitleSave={(newTitle) => { - saveTitle(newTitle); - }} /> ); } -export function Insight({ messages, title }: { messages: Message[]; title: string }) { +export function Insight({ + messages, + title, + dataTestSubj, +}: { + messages: Message[]; + title: string; + dataTestSubj?: string; +}) { const [hasOpened, setHasOpened] = useState(false); const connectors = useGenAIConnectors(); @@ -322,6 +156,7 @@ export function Insight({ messages, title }: { messages: Message[]; title: strin }} controls={} loading={connectors.loading || chatService.loading} + dataTestSubj={dataTestSubj} > {chatService.value ? ( diff --git a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx index b6c0b4db906b7..f288129c7ba9f 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/insight/insight_base.tsx @@ -32,6 +32,7 @@ export interface InsightBaseProps { onToggle: (isOpen: boolean) => void; children: React.ReactNode; loading?: boolean; + dataTestSubj?: string; } export function InsightBase({ @@ -44,6 +45,7 @@ export function InsightBase({ actions, onToggle, loading, + dataTestSubj = 'obsAiAssistantInsightButton', }: InsightBaseProps) { const { euiTheme } = useEuiTheme(); @@ -59,7 +61,7 @@ export function InsightBase({ id="obsAiAssistantInsight" arrowProps={{ css: { alignSelf: 'flex-start' } }} buttonContent={ - + diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx index 393bbeee28f8d..9af3e3cf285b4 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.stories.tsx @@ -71,7 +71,7 @@ export const ContentFailed: ComponentStoryObj = { onActionClick={async () => {}} /> ), - error: new Error(), + error: true, }, }; @@ -111,7 +111,7 @@ export const Controls: ComponentStoryObj = { onActionClick={async () => {}} /> ), - error: new Error(), + error: true, controls: {}} />, }, }; diff --git a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.tsx b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.tsx index 78a1e8fae4778..820ab2a55c271 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/message_panel/message_panel.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FailedToLoadResponse } from './failed_to_load_response'; interface Props { - error?: Error; + error?: boolean; body?: React.ReactNode; controls?: React.ReactNode; } diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts index a37624d441757..afd776dc13990 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_abortable_async.ts @@ -18,9 +18,9 @@ export type AbortableAsyncState = (T extends Promise : State) & { refresh: () => void }; export function useAbortableAsync( - fn: ({}: { signal: AbortSignal }) => T, + fn: ({}: { signal: AbortSignal }) => T | Promise, deps: any[], - options?: { clearValueOnNext?: boolean } + options?: { clearValueOnNext?: boolean; defaultValue?: () => T } ): AbortableAsyncState { const clearValueOnNext = options?.clearValueOnNext; @@ -30,7 +30,7 @@ export function useAbortableAsync( const [error, setError] = useState(); const [loading, setLoading] = useState(false); - const [value, setValue] = useState(); + const [value, setValue] = useState(options?.defaultValue); useEffect(() => { controllerRef.current.abort(); diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.test.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.test.ts new file mode 100644 index 0000000000000..22bc997a4c925 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.test.ts @@ -0,0 +1,464 @@ +/* + * Copyright 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 { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import { type RenderHookResult, renderHook, act } from '@testing-library/react-hooks'; +import { Subject } from 'rxjs'; +import { MessageRole } from '../../common'; +import type { ObservabilityAIAssistantChatService, PendingMessage } from '../types'; +import { type UseChatResult, useChat, type UseChatProps, ChatState } from './use_chat'; +import * as useKibanaModule from './use_kibana'; + +type MockedChatService = DeeplyMockedKeys; + +const mockChatService: MockedChatService = { + chat: jest.fn(), + executeFunction: jest.fn(), + getContexts: jest.fn().mockReturnValue([{ name: 'core', description: '' }]), + getFunctions: jest.fn().mockReturnValue([]), + hasFunction: jest.fn().mockReturnValue(false), + hasRenderFunction: jest.fn().mockReturnValue(true), + renderFunction: jest.fn(), +}; + +const addErrorMock = jest.fn(); + +jest.spyOn(useKibanaModule, 'useKibana').mockReturnValue({ + services: { + notifications: { + toasts: { + addError: addErrorMock, + }, + }, + }, +} as any); + +let hookResult: RenderHookResult; + +describe('useChat', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('initially', () => { + beforeEach(() => { + hookResult = renderHook(useChat, { + initialProps: { + connectorId: 'my-connector', + chatService: mockChatService, + initialMessages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'hello', + }, + }, + ], + } as UseChatProps, + }); + }); + + it('returns the initial messages including the system message', () => { + const { messages } = hookResult.result.current; + expect(messages.length).toBe(2); + expect(messages[0].message.role).toBe('system'); + expect(messages[1].message.content).toBe('hello'); + }); + + it('sets chatState to ready', () => { + expect(hookResult.result.current.state).toBe(ChatState.Ready); + }); + }); + + describe('when calling next()', () => { + let subject: Subject; + + beforeEach(() => { + hookResult = renderHook(useChat, { + initialProps: { + connectorId: 'my-connector', + chatService: mockChatService, + initialMessages: [], + } as UseChatProps, + }); + + subject = new Subject(); + + mockChatService.chat.mockReturnValueOnce(subject); + + act(() => { + hookResult.result.current.next([ + ...hookResult.result.current.messages, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'hello', + }, + }, + ]); + }); + }); + + it('sets the chatState to loading', () => { + expect(hookResult.result.current.state).toBe(ChatState.Loading); + }); + + describe('after asking for another response', () => { + beforeEach(() => { + act(() => { + hookResult.result.current.next([]); + subject.next({ + message: { + role: MessageRole.User, + content: 'goodbye', + }, + }); + subject.complete(); + }); + }); + + it('shows an empty list of messages', () => { + expect(hookResult.result.current.messages.length).toBe(1); + expect(hookResult.result.current.messages[0].message.role).toBe(MessageRole.System); + }); + + it('aborts the running request', () => { + expect(subject.observed).toBe(false); + }); + }); + + describe('after a partial response', () => { + it('updates the returned messages', () => { + act(() => { + subject.next({ + message: { + content: 'good', + role: MessageRole.Assistant, + }, + }); + }); + + expect(hookResult.result.current.messages[2].message.content).toBe('good'); + }); + }); + + describe('after a completed response', () => { + it('updates the returned messages and the loading state', () => { + act(() => { + subject.next({ + message: { + content: 'good', + role: MessageRole.Assistant, + }, + }); + subject.next({ + message: { + content: 'goodbye', + role: MessageRole.Assistant, + }, + }); + subject.complete(); + }); + + expect(hookResult.result.current.messages[2].message.content).toBe('goodbye'); + expect(hookResult.result.current.state).toBe(ChatState.Ready); + }); + }); + + describe('after aborting a response', () => { + beforeEach(() => { + act(() => { + subject.next({ + message: { + content: 'good', + role: MessageRole.Assistant, + }, + aborted: true, + }); + subject.complete(); + }); + }); + + it('shows the partial message and sets chatState to aborted', () => { + expect(hookResult.result.current.messages[2].message.content).toBe('good'); + expect(hookResult.result.current.state).toBe(ChatState.Aborted); + }); + + it('does not show an error toast', () => { + expect(addErrorMock).not.toHaveBeenCalled(); + }); + }); + + describe('after a response errors out', () => { + beforeEach(() => { + act(() => { + subject.next({ + message: { + content: 'good', + role: MessageRole.Assistant, + }, + error: new Error('foo'), + }); + subject.complete(); + }); + }); + + it('shows the partial message and sets chatState to error', () => { + expect(hookResult.result.current.messages[2].message.content).toBe('good'); + expect(hookResult.result.current.state).toBe(ChatState.Error); + }); + + it('shows an error toast', () => { + expect(addErrorMock).toHaveBeenCalled(); + }); + }); + + describe('after the LLM responds with a function call', () => { + let resolve: (data: any) => void; + let reject: (error: Error) => void; + + beforeEach(() => { + mockChatService.executeFunction.mockResolvedValueOnce( + new Promise((...args) => { + resolve = args[0]; + reject = args[1]; + }) + ); + + act(() => { + subject.next({ + message: { + content: '', + role: MessageRole.Assistant, + function_call: { + name: 'my_function', + arguments: JSON.stringify({ foo: 'bar' }), + trigger: MessageRole.Assistant, + }, + }, + }); + subject.complete(); + }); + }); + + it('the chat state stays loading', () => { + expect(hookResult.result.current.state).toBe(ChatState.Loading); + }); + + it('adds a message', () => { + const { messages } = hookResult.result.current; + + expect(messages.length).toBe(3); + expect(messages[2]).toEqual({ + '@timestamp': expect.any(String), + message: { + content: '', + function_call: { + arguments: JSON.stringify({ foo: 'bar' }), + name: 'my_function', + trigger: MessageRole.Assistant, + }, + role: MessageRole.Assistant, + }, + }); + }); + + describe('the function call succeeds', () => { + beforeEach(async () => { + subject = new Subject(); + mockChatService.chat.mockReturnValueOnce(subject); + + await act(async () => { + resolve({ content: { foo: 'bar' }, data: { bar: 'foo' } }); + }); + }); + + it('adds a message', () => { + const { messages } = hookResult.result.current; + + expect(messages.length).toBe(4); + expect(messages[3]).toEqual({ + '@timestamp': expect.any(String), + message: { + content: JSON.stringify({ foo: 'bar' }), + data: JSON.stringify({ bar: 'foo' }), + name: 'my_function', + role: MessageRole.User, + }, + }); + }); + + it('keeps the chat state in loading', () => { + expect(hookResult.result.current.state).toBe(ChatState.Loading); + }); + it('sends the function call back to the LLM for a response', () => { + expect(mockChatService.chat).toHaveBeenCalledTimes(2); + expect(mockChatService.chat).toHaveBeenLastCalledWith({ + connectorId: 'my-connector', + messages: hookResult.result.current.messages, + }); + }); + }); + + describe('the function call fails', () => { + beforeEach(async () => { + subject = new Subject(); + mockChatService.chat.mockReturnValue(subject); + + await act(async () => { + reject(new Error('connection error')); + }); + }); + + it('keeps the chat state in loading', () => { + expect(hookResult.result.current.state).toBe(ChatState.Loading); + }); + + it('adds a message', () => { + const { messages } = hookResult.result.current; + + expect(messages.length).toBe(4); + expect(messages[3]).toEqual({ + '@timestamp': expect.any(String), + message: { + content: JSON.stringify({ + message: 'Error: connection error', + error: {}, + }), + name: 'my_function', + role: MessageRole.User, + }, + }); + }); + + it('does not show an error toast', () => { + expect(addErrorMock).not.toHaveBeenCalled(); + }); + + it('sends the function call back to the LLM for a response', () => { + expect(mockChatService.chat).toHaveBeenCalledTimes(2); + expect(mockChatService.chat).toHaveBeenLastCalledWith({ + connectorId: 'my-connector', + messages: hookResult.result.current.messages, + }); + }); + }); + + describe('stop() is called', () => { + beforeEach(() => { + act(() => { + hookResult.result.current.stop(); + }); + }); + + it('sets the chatState to aborted', () => { + expect(hookResult.result.current.state).toBe(ChatState.Aborted); + }); + + it('has called the abort controller', () => { + const signal = mockChatService.executeFunction.mock.calls[0][0].signal; + + expect(signal.aborted).toBe(true); + }); + + it('is not updated after the promise is rejected', () => { + const numRenders = hookResult.result.all.length; + + act(() => { + reject(new Error('Request aborted')); + }); + + expect(numRenders).toBe(hookResult.result.all.length); + }); + + it('removes all subscribers', () => { + expect(subject.observed).toBe(false); + }); + }); + + describe('setMessages() is called', () => {}); + }); + }); + + describe('when calling next() with the recall function available', () => { + let subject: Subject; + + beforeEach(async () => { + hookResult = renderHook(useChat, { + initialProps: { + connectorId: 'my-connector', + chatService: mockChatService, + initialMessages: [], + } as UseChatProps, + }); + + subject = new Subject(); + + mockChatService.hasFunction.mockReturnValue(true); + mockChatService.executeFunction.mockResolvedValueOnce({ + content: [ + { + id: 'my_document', + text: 'My text', + }, + ], + }); + + mockChatService.chat.mockReturnValueOnce(subject); + + await act(async () => { + hookResult.result.current.next([ + ...hookResult.result.current.messages, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'hello', + }, + }, + ]); + }); + }); + + it('adds a user message and a recall function request', () => { + expect(hookResult.result.current.messages[1].message.content).toBe('hello'); + expect(hookResult.result.current.messages[2].message.function_call?.name).toBe('recall'); + expect(hookResult.result.current.messages[2].message.content).toBe(''); + expect(hookResult.result.current.messages[2].message.function_call?.arguments).toBe( + JSON.stringify({ queries: [], contexts: [] }) + ); + expect(hookResult.result.current.messages[3].message.name).toBe('recall'); + expect(hookResult.result.current.messages[3].message.content).toBe( + JSON.stringify([ + { + id: 'my_document', + text: 'My text', + }, + ]) + ); + }); + + it('executes the recall function', () => { + expect(mockChatService.executeFunction).toHaveBeenCalled(); + expect(mockChatService.executeFunction).toHaveBeenCalledWith({ + signal: expect.any(AbortSignal), + connectorId: 'my-connector', + args: JSON.stringify({ queries: [], contexts: [] }), + name: 'recall', + messages: [...hookResult.result.current.messages.slice(0, -1)], + }); + }); + + it('sends the user message, function request and recall response to the LLM', () => { + expect(mockChatService.chat).toHaveBeenCalledWith({ + connectorId: 'my-connector', + messages: [...hookResult.result.current.messages], + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts new file mode 100644 index 0000000000000..aeef36127f6c4 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_chat.ts @@ -0,0 +1,260 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { last } from 'lodash'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { isObservable } from 'rxjs'; +import { type Message, MessageRole } from '../../common'; +import { getAssistantSetupMessage } from '../service/get_assistant_setup_message'; +import type { ObservabilityAIAssistantChatService, PendingMessage } from '../types'; +import { useKibana } from './use_kibana'; +import { useOnce } from './use_once'; + +export enum ChatState { + Ready = 'ready', + Loading = 'loading', + Error = 'error', + Aborted = 'aborted', +} + +export interface UseChatResult { + messages: Message[]; + setMessages: (messages: Message[]) => void; + state: ChatState; + next: (messages: Message[]) => void; + stop: () => void; +} + +export interface UseChatProps { + initialMessages: Message[]; + chatService: ObservabilityAIAssistantChatService; + connectorId?: string; + onChatComplete?: (messages: Message[]) => void; +} + +export function useChat({ + initialMessages, + chatService, + connectorId, + onChatComplete, +}: UseChatProps): UseChatResult { + const [chatState, setChatState] = useState(ChatState.Ready); + + const systemMessage = useMemo(() => { + return getAssistantSetupMessage({ contexts: chatService.getContexts() }); + }, [chatService]); + + useOnce(initialMessages); + + const [messages, setMessages] = useState(initialMessages); + + const [pendingMessage, setPendingMessage] = useState(); + + const abortControllerRef = useRef(new AbortController()); + + const { + services: { notifications }, + } = useKibana(); + + const onChatCompleteRef = useRef(onChatComplete); + + onChatCompleteRef.current = onChatComplete; + + const handleSignalAbort = useCallback(() => { + setChatState(ChatState.Aborted); + }, []); + + const next = useCallback( + async (nextMessages: Message[]) => { + // make sure we ignore any aborts for the previous signal + abortControllerRef.current.signal.removeEventListener('abort', handleSignalAbort); + + // cancel running requests + abortControllerRef.current.abort(); + + const lastMessage = last(nextMessages); + + const allMessages = [ + systemMessage, + ...nextMessages.filter((message) => message.message.role !== MessageRole.System), + ]; + + setMessages(allMessages); + + if (!lastMessage || !connectorId) { + setChatState(ChatState.Ready); + onChatCompleteRef.current?.(nextMessages); + return; + } + + const isUserMessage = lastMessage.message.role === MessageRole.User; + const functionCall = lastMessage.message.function_call; + const isAssistantMessageWithFunctionRequest = + lastMessage.message.role === MessageRole.Assistant && functionCall && !!functionCall.name; + + const isFunctionResult = isUserMessage && !!lastMessage.message.name; + + const isRecallFunctionAvailable = chatService.hasFunction('recall'); + + if (!isUserMessage && !isAssistantMessageWithFunctionRequest) { + setChatState(ChatState.Ready); + onChatCompleteRef.current?.(nextMessages); + return; + } + + const abortController = (abortControllerRef.current = new AbortController()); + + abortController.signal.addEventListener('abort', handleSignalAbort); + + setChatState(ChatState.Loading); + + if (isUserMessage && !isFunctionResult && isRecallFunctionAvailable) { + const allMessagesWithRecall = allMessages.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'recall', + arguments: JSON.stringify({ queries: [], contexts: [] }), + trigger: MessageRole.Assistant, + }, + }, + }); + next(allMessagesWithRecall); + return; + } + + function handleError(error: Error) { + setChatState(ChatState.Error); + notifications.toasts.addError(error, { + title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', { + defaultMessage: 'Failed to load response from the AI Assistant', + }), + }); + } + + const response = isAssistantMessageWithFunctionRequest + ? await chatService + .executeFunction({ + name: functionCall.name, + signal: abortController.signal, + args: functionCall.arguments, + connectorId, + messages: allMessages, + }) + .catch((error) => { + return { + content: { + message: error.toString(), + error, + }, + data: undefined, + }; + }) + : chatService.chat({ + messages: allMessages, + connectorId, + }); + + if (abortController.signal.aborted) { + return; + } + + if (isObservable(response)) { + let localPendingMessage: PendingMessage = { + message: { + content: '', + role: MessageRole.User, + }, + }; + + const subscription = response.subscribe({ + next: (nextPendingMessage) => { + localPendingMessage = nextPendingMessage; + setPendingMessage(nextPendingMessage); + }, + complete: () => { + setPendingMessage(undefined); + const allMessagesWithResolved = allMessages.concat({ + message: { + ...localPendingMessage.message, + }, + '@timestamp': new Date().toISOString(), + }); + if (localPendingMessage.aborted) { + setChatState(ChatState.Aborted); + setMessages(allMessagesWithResolved); + } else if (localPendingMessage.error) { + handleError(localPendingMessage.error); + setMessages(allMessagesWithResolved); + } else { + next(allMessagesWithResolved); + } + }, + error: (error) => { + handleError(error); + }, + }); + + abortController.signal.addEventListener('abort', () => { + subscription.unsubscribe(); + }); + } else { + const allMessagesWithFunctionReply = allMessages.concat({ + '@timestamp': new Date().toISOString(), + message: { + name: functionCall!.name, + role: MessageRole.User, + content: JSON.stringify(response.content), + data: JSON.stringify(response.data), + }, + }); + next(allMessagesWithFunctionReply); + } + }, + [connectorId, chatService, handleSignalAbort, notifications.toasts, systemMessage] + ); + + useEffect(() => { + return () => { + abortControllerRef.current.abort(); + }; + }, []); + + const memoizedMessages = useMemo(() => { + const includingSystemMessage = [ + systemMessage, + ...messages.filter((message) => message.message.role !== MessageRole.System), + ]; + + return pendingMessage + ? includingSystemMessage.concat({ + ...pendingMessage, + '@timestamp': new Date().toISOString(), + }) + : includingSystemMessage; + }, [systemMessage, messages, pendingMessage]); + + const setMessagesWithAbort = useCallback((nextMessages: Message[]) => { + abortControllerRef.current.abort(); + setPendingMessage(undefined); + setChatState(ChatState.Ready); + setMessages(nextMessages); + }, []); + + return { + messages: memoizedMessages, + setMessages: setMessagesWithAbort, + state: chatState, + next, + stop: () => { + abortControllerRef.current.abort(); + }, + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.test.tsx b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.test.tsx new file mode 100644 index 0000000000000..884aaff592a4e --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.test.tsx @@ -0,0 +1,703 @@ +/* + * Copyright 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 { + useConversation, + type UseConversationProps, + type UseConversationResult, +} from './use_conversation'; +import { + act, + renderHook, + type RenderHookResult, + type WrapperComponent, +} from '@testing-library/react-hooks'; +import type { ObservabilityAIAssistantService, PendingMessage } from '../types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import { ObservabilityAIAssistantProvider } from '../context/observability_ai_assistant_provider'; +import * as useKibanaModule from './use_kibana'; +import { Message, MessageRole } from '../../common'; +import { ChatState } from './use_chat'; +import { createMockChatService } from '../service/create_mock_chat_service'; +import { Subject } from 'rxjs'; +import { EMPTY_CONVERSATION_TITLE } from '../i18n'; +import { merge, omit } from 'lodash'; + +let hookResult: RenderHookResult; + +type MockedService = DeeplyMockedKeys; + +const mockService: MockedService = { + callApi: jest.fn(), + getCurrentUser: jest.fn(), + getLicense: jest.fn(), + getLicenseManagementLocator: jest.fn(), + isEnabled: jest.fn(), + start: jest.fn(), +}; + +const mockChatService = createMockChatService(); + +const addErrorMock = jest.fn(); + +jest.spyOn(useKibanaModule, 'useKibana').mockReturnValue({ + services: { + notifications: { + toasts: { + addError: addErrorMock, + }, + }, + }, +} as any); + +describe('useConversation', () => { + let wrapper: WrapperComponent; + + beforeEach(() => { + jest.clearAllMocks(); + wrapper = ({ children }) => ( + + {children} + + ); + }); + + describe('with initial messages and a conversation id', () => { + beforeEach(() => { + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialMessages: [ + { + '@timestamp': new Date().toISOString(), + message: { content: '', role: MessageRole.User }, + }, + ], + initialConversationId: 'foo', + }, + wrapper, + }); + }); + it('throws an error', () => { + expect(hookResult.result.error).toBeTruthy(); + }); + }); + + describe('without initial messages and a conversation id', () => { + beforeEach(() => { + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + }, + wrapper, + }); + }); + + it('returns only the system message', () => { + expect(hookResult.result.current.messages).toEqual([ + { + '@timestamp': expect.any(String), + message: { + content: '', + role: MessageRole.System, + }, + }, + ]); + }); + + it('returns a ready state', () => { + expect(hookResult.result.current.state).toBe(ChatState.Ready); + }); + + it('does not call the fetch api', () => { + expect(mockService.callApi).not.toHaveBeenCalled(); + }); + }); + + describe('with initial messages', () => { + beforeEach(() => { + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialMessages: [ + { + '@timestamp': new Date().toISOString(), + message: { + content: 'Test', + role: MessageRole.User, + }, + }, + ], + }, + wrapper, + }); + }); + + it('returns the system message and the initial messages', () => { + expect(hookResult.result.current.messages).toEqual([ + { + '@timestamp': expect.any(String), + message: { + content: '', + role: MessageRole.System, + }, + }, + { + '@timestamp': expect.any(String), + message: { + content: 'Test', + role: MessageRole.User, + }, + }, + ]); + }); + }); + + describe('with a conversation id that successfully loads', () => { + beforeEach(async () => { + mockService.callApi.mockResolvedValueOnce({ + conversation: { + id: 'my-conversation-id', + }, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'User', + }, + }, + ], + }); + + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialConversationId: 'my-conversation-id', + }, + wrapper, + }); + + await act(async () => {}); + }); + + it('returns the loaded conversation', () => { + expect(hookResult.result.current.conversation.value).toEqual({ + conversation: { + id: 'my-conversation-id', + }, + messages: [ + { + '@timestamp': expect.any(String), + message: { + content: 'System', + role: MessageRole.System, + }, + }, + { + '@timestamp': expect.any(String), + message: { + content: 'User', + role: MessageRole.User, + }, + }, + ], + }); + }); + + it('sets messages to the messages of the conversation', () => { + expect(hookResult.result.current.messages).toEqual([ + { + '@timestamp': expect.any(String), + message: { + content: expect.any(String), + role: MessageRole.System, + }, + }, + { + '@timestamp': expect.any(String), + message: { + content: 'User', + role: MessageRole.User, + }, + }, + ]); + }); + + it('overrides the system message', () => { + expect(hookResult.result.current.messages[0].message.content).toBe(''); + }); + }); + + describe('with a conversation id that fails to load', () => { + beforeEach(async () => { + mockService.callApi.mockRejectedValueOnce(new Error('failed to load')); + + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialConversationId: 'my-conversation-id', + }, + wrapper, + }); + + await act(async () => {}); + }); + + it('returns an error', () => { + expect(hookResult.result.current.conversation.error).toBeTruthy(); + }); + + it('resets the messages', () => { + expect(hookResult.result.current.messages.length).toBe(1); + }); + }); + + describe('when chat completes without an initial conversation id', () => { + let subject: Subject; + const expectedMessages = [ + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.System, + content: '', + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.Assistant, + content: 'Goodbye', + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.User, + content: 'Hello again', + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.Assistant, + content: 'Goodbye again', + }, + }, + ]; + beforeEach(() => { + subject = new Subject(); + mockService.callApi.mockImplementation(async (endpoint, request) => + merge( + { + conversation: { + id: 'my-conversation-id', + }, + messages: expectedMessages, + }, + (request as any).params.body + ) + ); + + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialMessages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: 'Goodbye', + }, + }, + ], + }, + wrapper, + }); + + mockChatService.chat.mockImplementationOnce(() => { + return subject; + }); + + act(() => { + hookResult.result.current.next( + hookResult.result.current.messages.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello again', + }, + }) + ); + }); + }); + + describe('when chat completes with an error', () => { + beforeEach(async () => { + mockService.callApi.mockClear(); + act(() => { + subject.next({ + message: { + role: MessageRole.Assistant, + content: 'Goodbye', + }, + error: new Error(), + }); + subject.complete(); + }); + await act(async () => {}); + }); + + it('does not store the conversation', () => { + expect(mockService.callApi).not.toHaveBeenCalled(); + }); + }); + + describe('when chat completes without an error', () => { + beforeEach(async () => { + act(() => { + subject.next({ + message: { + role: MessageRole.Assistant, + content: 'Goodbye again', + }, + }); + subject.complete(); + }); + + await act(async () => {}); + }); + it('the conversation is created including the initial messages', async () => { + expect(mockService.callApi.mock.calls[0]).toEqual([ + 'POST /internal/observability_ai_assistant/conversation', + { + params: { + body: { + conversation: { + '@timestamp': expect.any(String), + conversation: { + title: EMPTY_CONVERSATION_TITLE, + }, + messages: expectedMessages, + labels: {}, + numeric_labels: {}, + public: false, + }, + }, + }, + signal: null, + }, + ]); + + expect(hookResult.result.current.conversation.error).toBeUndefined(); + + expect(hookResult.result.current.messages).toEqual(expectedMessages); + }); + }); + }); + + describe('when chat completes with an initial conversation id', () => { + let subject: Subject; + + const initialMessages: Message[] = [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: '', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'user', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: 'assistant', + }, + }, + ]; + + beforeEach(async () => { + mockService.callApi.mockImplementation(async (endpoint, request) => ({ + '@timestamp': new Date().toISOString(), + conversation: { + id: 'my-conversation-id', + title: EMPTY_CONVERSATION_TITLE, + }, + labels: {}, + numeric_labels: {}, + public: false, + messages: initialMessages, + })); + + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialConversationId: 'my-conversation-id', + }, + wrapper, + }); + + await act(async () => {}); + }); + + it('the conversation is loadeded', async () => { + expect(mockService.callApi.mock.calls[0]).toEqual([ + 'GET /internal/observability_ai_assistant/conversation/{conversationId}', + { + signal: expect.anything(), + params: { + path: { + conversationId: 'my-conversation-id', + }, + }, + }, + ]); + + expect(hookResult.result.current.messages).toEqual( + initialMessages.map((msg) => ({ ...msg, '@timestamp': expect.any(String) })) + ); + }); + + describe('after chat completes', () => { + const nextUserMessage: Message = { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello again', + }, + }; + + const nextAssistantMessage: Message = { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: 'Goodbye again', + }, + }; + + beforeEach(async () => { + mockService.callApi.mockClear(); + subject = new Subject(); + + mockChatService.chat.mockImplementationOnce(() => { + return subject; + }); + + act(() => { + hookResult.result.current.next( + hookResult.result.current.messages.concat(nextUserMessage) + ); + subject.next(omit(nextAssistantMessage, '@timestamp')); + subject.complete(); + }); + + await act(async () => {}); + }); + + it('saves the updated message', () => { + expect(mockService.callApi.mock.calls[0]).toEqual([ + 'PUT /internal/observability_ai_assistant/conversation/{conversationId}', + { + params: { + path: { + conversationId: 'my-conversation-id', + }, + body: { + conversation: { + '@timestamp': expect.any(String), + conversation: { + title: EMPTY_CONVERSATION_TITLE, + id: 'my-conversation-id', + }, + messages: initialMessages + .concat([nextUserMessage, nextAssistantMessage]) + .map((msg) => ({ ...msg, '@timestamp': expect.any(String) })), + labels: {}, + numeric_labels: {}, + public: false, + }, + }, + }, + signal: null, + }, + ]); + }); + }); + }); + + describe('when the title is updated', () => { + describe('without a stored conversation', () => { + beforeEach(() => { + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialMessages: [ + { + '@timestamp': new Date().toISOString(), + message: { content: '', role: MessageRole.User }, + }, + ], + initialConversationId: 'foo', + }, + wrapper, + }); + }); + + it('throws an error', () => { + expect(() => hookResult.result.current.saveTitle('my-new-title')).toThrow(); + }); + }); + + describe('with a stored conversation', () => { + let resolve: (value: unknown) => void; + beforeEach(async () => { + mockService.callApi.mockImplementation(async (endpoint, request) => { + if ( + endpoint === 'PUT /internal/observability_ai_assistant/conversation/{conversationId}' + ) { + return new Promise((_resolve) => { + resolve = _resolve; + }); + } + return { + '@timestamp': new Date().toISOString(), + conversation: { + id: 'my-conversation-id', + title: EMPTY_CONVERSATION_TITLE, + }, + labels: {}, + numeric_labels: {}, + public: false, + messages: [], + }; + }); + + await act(async () => { + hookResult = renderHook(useConversation, { + initialProps: { + chatService: mockChatService, + connectorId: 'my-connector', + initialConversationId: 'my-conversation-id', + }, + wrapper, + }); + }); + }); + + it('does not throw an error', () => { + expect(() => hookResult.result.current.saveTitle('my-new-title')).not.toThrow(); + }); + + it('calls the update API', async () => { + act(() => { + hookResult.result.current.saveTitle('my-new-title'); + }); + + expect(resolve).not.toBeUndefined(); + + expect(mockService.callApi.mock.calls[1]).toEqual([ + 'PUT /internal/observability_ai_assistant/conversation/{conversationId}', + { + signal: null, + params: { + path: { + conversationId: 'my-conversation-id', + }, + body: { + conversation: { + '@timestamp': expect.any(String), + conversation: { + title: 'my-new-title', + id: 'my-conversation-id', + }, + labels: expect.anything(), + messages: expect.anything(), + numeric_labels: expect.anything(), + public: expect.anything(), + }, + }, + }, + }, + ]); + + mockService.callApi.mockImplementation(async (endpoint, request) => { + return { + '@timestamp': new Date().toISOString(), + conversation: { + id: 'my-conversation-id', + title: 'my-new-title', + }, + labels: {}, + numeric_labels: {}, + public: false, + messages: [], + }; + }); + + await act(async () => { + resolve({ + conversation: { + title: 'my-new-title', + }, + }); + }); + + expect(mockService.callApi.mock.calls[2]).toEqual([ + 'GET /internal/observability_ai_assistant/conversation/{conversationId}', + { + signal: expect.anything(), + params: { + path: { + conversationId: 'my-conversation-id', + }, + }, + }, + ]); + + expect(hookResult.result.current.conversation.value?.conversation.title).toBe( + 'my-new-title' + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts index 6970c53e28bf1..c753f7c7b1929 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_conversation.ts @@ -6,145 +6,127 @@ */ import { i18n } from '@kbn/i18n'; import { merge, omit } from 'lodash'; -import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'; -import { type Conversation, type Message } from '../../common'; -import { ConversationCreateRequest, MessageRole } from '../../common/types'; -import { getAssistantSetupMessage } from '../service/get_assistant_setup_message'; -import { ObservabilityAIAssistantChatService } from '../types'; +import { useState } from 'react'; +import type { Conversation, Message } from '../../common'; +import type { ConversationCreateRequest } from '../../common/types'; +import { EMPTY_CONVERSATION_TITLE } from '../i18n'; +import type { ObservabilityAIAssistantChatService } from '../types'; import { useAbortableAsync, type AbortableAsyncState } from './use_abortable_async'; +import { useChat, UseChatResult } from './use_chat'; import { useKibana } from './use_kibana'; import { useObservabilityAIAssistant } from './use_observability_ai_assistant'; -import { createNewConversation } from './use_timeline'; +import { useOnce } from './use_once'; + +function createNewConversation({ + title = EMPTY_CONVERSATION_TITLE, +}: { title?: string } = {}): ConversationCreateRequest { + return { + '@timestamp': new Date().toISOString(), + messages: [], + conversation: { + title, + }, + labels: {}, + numeric_labels: {}, + public: false, + }; +} + +export interface UseConversationProps { + initialConversationId?: string; + initialMessages?: Message[]; + initialTitle?: string; + chatService: ObservabilityAIAssistantChatService; + connectorId: string | undefined; + onConversationUpdate?: (conversation: Conversation) => void; +} + +export type UseConversationResult = { + conversation: AbortableAsyncState; + saveTitle: (newTitle: string) => void; +} & Omit; + +const DEFAULT_INITIAL_MESSAGES: Message[] = []; export function useConversation({ - conversationId, + initialConversationId: initialConversationIdFromProps, + initialMessages: initialMessagesFromProps = DEFAULT_INITIAL_MESSAGES, + initialTitle: initialTitleFromProps, chatService, connectorId, - initialMessages = [], -}: { - conversationId?: string; - chatService?: ObservabilityAIAssistantChatService; // will eventually resolve to a non-nullish value - connectorId: string | undefined; - initialMessages?: Message[]; -}): { - conversation: AbortableAsyncState; - displayedMessages: Message[]; - setDisplayedMessages: Dispatch>; - getSystemMessage: () => Message; - save: (messages: Message[], handleRefreshConversations?: () => void) => Promise; - saveTitle: ( - title: string, - handleRefreshConversations?: () => void - ) => Promise; -} { + onConversationUpdate, +}: UseConversationProps): UseConversationResult { const service = useObservabilityAIAssistant(); const { services: { notifications }, } = useKibana(); - const [displayedMessages, setDisplayedMessages] = useState(initialMessages); - - const getSystemMessage = useCallback(() => { - return getAssistantSetupMessage({ contexts: chatService?.getContexts() || [] }); - }, [chatService]); + const initialConversationId = useOnce(initialConversationIdFromProps); + const initialMessages = useOnce(initialMessagesFromProps); + const initialTitle = useOnce(initialTitleFromProps); - const displayedMessagesWithHardcodedSystemMessage = useMemo(() => { - if (!chatService) { - return displayedMessages; - } + if (initialMessages.length && initialConversationId) { + throw new Error('Cannot set initialMessages if initialConversationId is set'); + } - const systemMessage = getSystemMessage(); - - if (displayedMessages[0]?.message.role === MessageRole.User) { - return [systemMessage, ...displayedMessages]; - } - - return [systemMessage, ...displayedMessages.slice(1)]; - }, [displayedMessages, chatService, getSystemMessage]); + const update = (nextConversationObject: Conversation) => { + return service + .callApi(`PUT /internal/observability_ai_assistant/conversation/{conversationId}`, { + signal: null, + params: { + path: { + conversationId: nextConversationObject.conversation.id, + }, + body: { + conversation: merge( + { + '@timestamp': nextConversationObject['@timestamp'], + conversation: { + id: nextConversationObject.conversation.id, + }, + }, + omit(nextConversationObject, 'conversation.last_updated', 'namespace', 'user') + ), + }, + }, + }) + .catch((err) => { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.observabilityAiAssistant.errorUpdatingConversation', { + defaultMessage: 'Could not update conversation', + }), + }); + throw err; + }); + }; - const conversation: AbortableAsyncState = - useAbortableAsync( - ({ signal }) => { - if (!conversationId) { - const nextConversation = createNewConversation({ - contexts: chatService?.getContexts() || [], - }); - setDisplayedMessages(nextConversation.messages); - return nextConversation; - } + const save = (nextMessages: Message[]) => { + const conversationObject = conversation.value!; - return service - .callApi('GET /internal/observability_ai_assistant/conversation/{conversationId}', { - signal, - params: { path: { conversationId } }, - }) - .then((nextConversation) => { - setDisplayedMessages(nextConversation.messages); - return nextConversation; - }) - .catch((error) => { - setDisplayedMessages([]); - throw error; - }); - }, - [conversationId, chatService] - ); + const nextConversationObject = merge({}, omit(conversationObject, 'messages'), { + messages: nextMessages, + }); - return { - conversation, - displayedMessages: displayedMessagesWithHardcodedSystemMessage, - setDisplayedMessages, - getSystemMessage, - save: (messages: Message[], handleRefreshConversations?: () => void) => { - const conversationObject = conversation.value!; - - return conversationId - ? service - .callApi(`PUT /internal/observability_ai_assistant/conversation/{conversationId}`, { - signal: null, - params: { - path: { - conversationId, - }, - body: { - conversation: merge( - { - '@timestamp': conversationObject['@timestamp'], - conversation: { - id: conversationId, - }, - }, - omit( - conversationObject, - 'conversation.last_updated', - 'namespace', - 'user', - 'messages' - ), - { messages } - ), - }, - }, - }) - .catch((err) => { - notifications.toasts.addError(err, { - title: i18n.translate('xpack.observabilityAiAssistant.errorUpdatingConversation', { - defaultMessage: 'Could not update conversation', - }), - }); - throw err; - }) + return ( + displayedConversationId + ? update( + merge( + { conversation: { id: displayedConversationId } }, + nextConversationObject + ) as Conversation + ) : service .callApi(`POST /internal/observability_ai_assistant/conversation`, { signal: null, params: { body: { - conversation: merge({}, conversationObject, { messages }), + conversation: nextConversationObject, }, }, }) .then((nextConversation) => { + setDisplayedConversationId(nextConversation.conversation.id); if (connectorId) { service .callApi( @@ -162,7 +144,7 @@ export function useConversation({ } ) .then(() => { - handleRefreshConversations?.(); + onConversationUpdate?.(nextConversation); return conversation.refresh(); }); } @@ -175,27 +157,78 @@ export function useConversation({ }), }); throw err; - }); + }) + ).then((nextConversation) => { + onConversationUpdate?.(nextConversation); + return nextConversation; + }); + }; + + const { next, messages, setMessages, state, stop } = useChat({ + initialMessages, + chatService, + connectorId, + onChatComplete: (nextMessages) => { + save(nextMessages); }, - saveTitle: (title: string, handleRefreshConversations?: () => void) => { - if (conversationId) { + }); + + const [displayedConversationId, setDisplayedConversationId] = useState(initialConversationId); + + const conversation: AbortableAsyncState = + useAbortableAsync( + ({ signal }) => { + if (!displayedConversationId) { + const nextConversation = createNewConversation({ title: initialTitle }); + return nextConversation; + } + return service - .callApi('PUT /internal/observability_ai_assistant/conversation/{conversationId}/title', { - signal: null, - params: { - path: { - conversationId, - }, - body: { - title, - }, - }, + .callApi('GET /internal/observability_ai_assistant/conversation/{conversationId}', { + signal, + params: { path: { conversationId: displayedConversationId } }, + }) + .then((nextConversation) => { + setMessages(nextConversation.messages); + return nextConversation; }) - .then(() => { - handleRefreshConversations?.(); + .catch((error) => { + setMessages([]); + throw error; }); + }, + [displayedConversationId, initialTitle], + { + defaultValue: () => { + if (!displayedConversationId) { + const nextConversation = createNewConversation({ title: initialTitle }); + return nextConversation; + } + return undefined; + }, + } + ); + + return { + conversation, + state, + next, + stop, + messages, + saveTitle: (title: string) => { + if (!displayedConversationId || !conversation.value) { + throw new Error('Cannot save title if conversation is not stored'); } - return Promise.resolve(); + const nextConversation = merge({}, conversation.value as Conversation, { + conversation: { title }, + }); + return update(nextConversation) + .then(() => { + return conversation.refresh(); + }) + .then(() => { + onConversationUpdate?.(nextConversation); + }); }, }; } diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts index 8e8f437a87fb2..6414e7f604f63 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_current_user.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser } from '@kbn/security-plugin/common'; import { useEffect, useState } from 'react'; import { useObservabilityAIAssistant } from './use_observability_ai_assistant'; diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/constants.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_force_update.ts similarity index 60% rename from x-pack/plugins/observability/common/custom_threshold_rule/constants.ts rename to x-pack/plugins/observability_ai_assistant/public/hooks/use_force_update.ts index 7f7c3a5bcbb3f..479a043b847c3 100644 --- a/x-pack/plugins/observability/common/custom_threshold_rule/constants.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_force_update.ts @@ -5,13 +5,12 @@ * 2.0. */ -export const METRIC_EXPLORER_AGGREGATIONS = [ - 'avg', - 'max', - 'min', - 'cardinality', - 'count', - 'sum', -] as const; +import { useState } from 'react'; -export const CUSTOM_AGGREGATOR = 'custom'; +export function useForceUpdate() { + const [_, setCounter] = useState(0); + + return () => { + setCounter((prev) => prev + 1); + }; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx b/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx index d7c76e0ab4f85..24c83d3fa8eb4 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_knowledge_base.tsx @@ -57,6 +57,7 @@ export function useKnowledgeBase(): UseKnowledgeBaseResult { text: i18n.translate('xpack.observabilityAiAssistant.knowledgeBaseReadyContentReload', { defaultMessage: 'A page reload is needed to be able to use it.', }), + toastLifeTimeMs: Number.MAX_VALUE, }); }) .catch((error) => { diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_once.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_once.ts new file mode 100644 index 0000000000000..00dab01456af0 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_once.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 { useRef } from 'react'; + +export function useOnce(variable: T): T { + const ref = useRef(variable); + + if (ref.current !== variable) { + // eslint-disable-next-line no-console + console.trace( + `Variable changed from ${ref.current} to ${variable}, but only the initial value will be taken into account` + ); + } + + return ref.current; +} diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts deleted file mode 100644 index 6ad1d0746a517..0000000000000 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.test.ts +++ /dev/null @@ -1,611 +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 { FindActionResult } from '@kbn/actions-plugin/server'; -import { AbortError } from '@kbn/kibana-utils-plugin/common'; -import { - act, - renderHook, - type Renderer, - type RenderHookResult, -} from '@testing-library/react-hooks'; -import { BehaviorSubject, Subject } from 'rxjs'; -import { MessageRole } from '../../common'; -import { ChatTimelineItem } from '../components/chat/chat_timeline'; -import type { PendingMessage } from '../types'; -import { useTimeline, UseTimelineResult } from './use_timeline'; - -type HookProps = Parameters[0]; - -const WAIT_OPTIONS = { timeout: 1500 }; - -jest.mock('./use_kibana', () => ({ - useKibana: () => ({ - services: { - notifications: { - toasts: { - addError: jest.fn(), - }, - }, - }, - }), -})); - -describe('useTimeline', () => { - let hookResult: RenderHookResult>; - - describe('with an empty conversation', () => { - beforeAll(() => { - hookResult = renderHook((props) => useTimeline(props), { - initialProps: { - connectors: { - loading: false, - selectedConnector: 'OpenAI', - selectConnector: () => {}, - connectors: [{ id: 'OpenAI' }] as FindActionResult[], - }, - chatService: {}, - messages: [], - onChatComplete: jest.fn(), - onChatUpdate: jest.fn(), - } as unknown as HookProps, - }); - }); - it('renders the correct timeline items', () => { - expect(hookResult.result.current.items.length).toEqual(1); - - expect(hookResult.result.current.items[0]).toEqual({ - display: { - collapsed: false, - hide: false, - }, - actions: { - canCopy: false, - canEdit: false, - canRegenerate: false, - canGiveFeedback: false, - }, - role: MessageRole.User, - title: 'started a conversation', - loading: false, - id: expect.any(String), - }); - }); - }); - - describe('with an existing conversation', () => { - beforeAll(() => { - hookResult = renderHook((props) => useTimeline(props), { - initialProps: { - messages: [ - { - message: { - role: MessageRole.System, - content: 'You are a helpful assistant for Elastic Observability', - }, - }, - { - message: { - role: MessageRole.User, - content: 'hello', - }, - }, - { - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'recall', - trigger: MessageRole.User, - }, - }, - }, - { - message: { - name: 'recall', - role: MessageRole.User, - content: '', - }, - }, - { - message: { - content: 'goodbye', - function_call: { - name: '', - arguments: '', - trigger: MessageRole.Assistant, - }, - role: MessageRole.Assistant, - }, - }, - ], - connectors: { - selectedConnector: 'foo', - }, - chatService: { - chat: () => {}, - hasRenderFunction: () => {}, - hasFunction: () => {}, - }, - } as unknown as HookProps, - }); - }); - it('renders the correct timeline items', () => { - expect(hookResult.result.current.items.length).toEqual(4); - - expect(hookResult.result.current.items[1]).toEqual({ - actions: { canCopy: true, canEdit: true, canGiveFeedback: false, canRegenerate: false }, - content: 'hello', - currentUser: undefined, - display: { collapsed: false, hide: false }, - element: undefined, - function_call: undefined, - id: expect.any(String), - loading: false, - role: MessageRole.User, - title: '', - }); - - expect(hookResult.result.current.items[3]).toEqual({ - actions: { canCopy: true, canEdit: false, canGiveFeedback: false, canRegenerate: true }, - content: 'goodbye', - currentUser: undefined, - display: { collapsed: false, hide: false }, - element: undefined, - function_call: { - arguments: '', - name: '', - trigger: MessageRole.Assistant, - }, - id: expect.any(String), - loading: false, - role: MessageRole.Assistant, - title: '', - }); - - // Items that are function calls are collapsed into an array. - - // 'title' is a component. This throws Jest for a loop. - const collapsedItemsWithoutTitle = ( - hookResult.result.current.items[2] as ChatTimelineItem[] - ).map(({ title, ...rest }) => rest); - - expect(collapsedItemsWithoutTitle).toEqual([ - { - display: { - collapsed: true, - hide: false, - }, - actions: { - canCopy: true, - canEdit: true, - canRegenerate: false, - canGiveFeedback: false, - }, - currentUser: undefined, - function_call: { - name: 'recall', - trigger: MessageRole.User, - }, - role: MessageRole.User, - content: `\`\`\` -{ - \"name\": \"recall\" -} -\`\`\``, - loading: false, - id: expect.any(String), - }, - { - display: { - collapsed: true, - hide: false, - }, - actions: { - canCopy: true, - canEdit: false, - canRegenerate: false, - canGiveFeedback: false, - }, - currentUser: undefined, - function_call: undefined, - role: MessageRole.User, - content: `\`\`\` -{} -\`\`\``, - loading: false, - id: expect.any(String), - }, - ]); - }); - }); - - describe('when submitting a new prompt', () => { - let subject: Subject; - - let props: Omit & { - onChatUpdate: jest.MockedFn; - onChatComplete: jest.MockedFn; - chatService: Omit & { - executeFunction: jest.MockedFn; - }; - }; - - beforeEach(() => { - props = { - messages: [], - connectors: { - selectedConnector: 'foo', - }, - chatService: { - chat: jest.fn().mockImplementation(() => { - subject = new BehaviorSubject({ - message: { - role: MessageRole.Assistant, - content: '', - }, - }); - return subject; - }), - executeFunction: jest.fn(), - hasFunction: jest.fn(), - hasRenderFunction: jest.fn(), - }, - onChatUpdate: jest.fn().mockImplementation((messages) => { - props = { ...props, messages }; - hookResult.rerender(props as unknown as HookProps); - }), - onChatComplete: jest.fn(), - } as any; - - hookResult = renderHook((nextProps) => useTimeline(nextProps), { - initialProps: props as unknown as HookProps, - }); - }); - - describe("and it's loading", () => { - beforeEach(() => { - act(() => { - hookResult.result.current.onSubmit({ - '@timestamp': new Date().toISOString(), - message: { role: MessageRole.User, content: 'Hello' }, - }); - }); - }); - - it('adds two items of which the last one is loading', async () => { - expect((hookResult.result.current.items[0] as ChatTimelineItem).role).toEqual( - MessageRole.User - ); - expect((hookResult.result.current.items[1] as ChatTimelineItem).role).toEqual( - MessageRole.User - ); - - expect((hookResult.result.current.items[2] as ChatTimelineItem).role).toEqual( - MessageRole.Assistant - ); - - expect(hookResult.result.current.items[1]).toMatchObject({ - role: MessageRole.User, - content: 'Hello', - loading: false, - }); - - expect(hookResult.result.current.items[2]).toMatchObject({ - role: MessageRole.Assistant, - content: '', - loading: true, - actions: { - canRegenerate: false, - canGiveFeedback: false, - }, - }); - - expect(hookResult.result.current.items.length).toBe(3); - - expect(hookResult.result.current.items[2]).toMatchObject({ - role: MessageRole.Assistant, - content: '', - loading: true, - actions: { - canRegenerate: false, - canGiveFeedback: false, - }, - }); - }); - - describe('and it pushes the next part', () => { - beforeEach(() => { - act(() => { - subject.next({ message: { role: MessageRole.Assistant, content: 'Goodbye' } }); - }); - }); - - it('adds the partial response', () => { - expect(hookResult.result.current.items[2]).toMatchObject({ - role: MessageRole.Assistant, - content: 'Goodbye', - loading: true, - actions: { - canRegenerate: false, - canGiveFeedback: false, - }, - }); - }); - - describe('and it completes', () => { - beforeEach(async () => { - act(() => { - subject.complete(); - }); - - await hookResult.waitForNextUpdate(WAIT_OPTIONS); - }); - - it('adds the completed message', () => { - expect(hookResult.result.current.items[2]).toMatchObject({ - role: MessageRole.Assistant, - content: 'Goodbye', - loading: false, - actions: { - canRegenerate: true, - canGiveFeedback: false, - }, - }); - }); - - describe('and the user edits a message', () => { - beforeEach(() => { - act(() => { - hookResult.result.current.onEdit( - hookResult.result.current.items[1] as ChatTimelineItem, - { - '@timestamp': new Date().toISOString(), - message: { content: 'Edited message', role: MessageRole.User }, - } - ); - subject.next({ message: { role: MessageRole.Assistant, content: '' } }); - subject.complete(); - }); - }); - - it('calls onChatUpdate with the edited message', () => { - expect(hookResult.result.current.items.length).toEqual(4); - expect((hookResult.result.current.items[2] as ChatTimelineItem).content).toEqual( - 'Edited message' - ); - expect((hookResult.result.current.items[3] as ChatTimelineItem).content).toEqual(''); - }); - }); - }); - }); - - describe('and it is being aborted', () => { - beforeEach(() => { - act(() => { - subject.next({ message: { role: MessageRole.Assistant, content: 'My partial' } }); - subject.next({ - message: { - role: MessageRole.Assistant, - content: 'My partial', - }, - aborted: true, - error: new AbortError(), - }); - subject.complete(); - }); - }); - - it('adds the partial response', async () => { - expect(hookResult.result.current.items.length).toBe(3); - - expect(hookResult.result.current.items[2]).toEqual({ - actions: { - canEdit: false, - canRegenerate: true, - canGiveFeedback: false, - canCopy: true, - }, - display: { - collapsed: false, - hide: false, - }, - content: 'My partial', - id: expect.any(String), - loading: false, - title: '', - role: MessageRole.Assistant, - error: expect.any(AbortError), - }); - }); - - describe('and it is being regenerated', () => { - beforeEach(() => { - act(() => { - hookResult.result.current.onRegenerate( - hookResult.result.current.items[2] as ChatTimelineItem - ); - subject.next({ message: { role: MessageRole.Assistant, content: '' } }); - }); - }); - - it('updates the last item in the array to be loading', () => { - expect(hookResult.result.current.items.length).toEqual(3); - - expect(hookResult.result.current.items[2]).toEqual({ - display: { - hide: false, - collapsed: false, - }, - actions: { - canCopy: true, - canEdit: false, - canRegenerate: false, - canGiveFeedback: false, - }, - content: '', - id: expect.any(String), - loading: true, - title: '', - role: MessageRole.Assistant, - }); - }); - - describe('and it is regenerated again', () => { - beforeEach(async () => { - act(() => { - hookResult.result.current.onStopGenerating(); - }); - - act(() => { - hookResult.result.current.onRegenerate( - hookResult.result.current.items[2] as ChatTimelineItem - ); - }); - }); - - it('updates the last item to be not loading again', async () => { - expect(hookResult.result.current.items.length).toBe(3); - - expect(hookResult.result.current.items[2]).toEqual({ - actions: { - canCopy: true, - canEdit: false, - canRegenerate: false, - canGiveFeedback: false, - }, - display: { - collapsed: false, - hide: false, - }, - content: '', - id: expect.any(String), - loading: true, - title: '', - role: MessageRole.Assistant, - }); - - act(() => { - subject.next({ message: { role: MessageRole.Assistant, content: 'Regenerated' } }); - subject.complete(); - }); - - await hookResult.waitForNextUpdate(WAIT_OPTIONS); - - expect(hookResult.result.current.items.length).toBe(3); - - expect(hookResult.result.current.items[2]).toEqual({ - display: { - collapsed: false, - hide: false, - }, - actions: { - canCopy: true, - canEdit: false, - canRegenerate: true, - canGiveFeedback: false, - }, - content: 'Regenerated', - currentUser: undefined, - function_call: undefined, - id: expect.any(String), - element: undefined, - loading: false, - title: '', - role: MessageRole.Assistant, - }); - }); - }); - }); - }); - - describe('and a function call is returned', () => { - it('the function call is executed and its response is sent as a user reply', async () => { - jest.clearAllMocks(); - - act(() => { - subject.next({ - message: { - role: MessageRole.Assistant, - function_call: { - trigger: MessageRole.Assistant, - name: 'my_function', - arguments: '{}', - }, - }, - }); - subject.complete(); - }); - - props.chatService.executeFunction.mockResolvedValueOnce({ - content: { - message: 'my-response', - }, - }); - - await hookResult.waitForNextUpdate(WAIT_OPTIONS); - - expect(props.onChatUpdate).toHaveBeenCalledTimes(2); - - expect( - props.onChatUpdate.mock.calls[0][0].map( - (msg) => msg.message.content || msg.message.function_call?.name - ) - ).toEqual(['Hello', 'my_function']); - - expect( - props.onChatUpdate.mock.calls[1][0].map( - (msg) => msg.message.content || msg.message.function_call?.name - ) - ).toEqual(['Hello', 'my_function', JSON.stringify({ message: 'my-response' })]); - - expect(props.onChatComplete).not.toHaveBeenCalled(); - - expect(props.chatService.executeFunction).toHaveBeenCalledWith({ - name: 'my_function', - args: '{}', - connectorId: 'foo', - messages: [ - { - '@timestamp': expect.any(String), - message: { - content: 'Hello', - role: MessageRole.User, - }, - }, - ], - signal: expect.any(Object), - }); - - act(() => { - subject.next({ - message: { - role: MessageRole.Assistant, - content: 'looks like my-function returned my-response', - }, - }); - subject.complete(); - }); - - await hookResult.waitForNextUpdate(WAIT_OPTIONS); - - expect(props.onChatComplete).toHaveBeenCalledTimes(1); - - expect( - props.onChatComplete.mock.calls[0][0].map( - (msg) => msg.message.content || msg.message.function_call?.name - ) - ).toEqual([ - 'Hello', - 'my_function', - JSON.stringify({ message: 'my-response' }), - 'looks like my-function returned my-response', - ]); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts deleted file mode 100644 index 64d82cabb9437..0000000000000 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_timeline.ts +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { AbortError } from '@kbn/kibana-utils-plugin/common'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { flatten, last } from 'lodash'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import usePrevious from 'react-use/lib/usePrevious'; -import { isObservable, Observable, Subscription } from 'rxjs'; -import { - ContextDefinition, - MessageRole, - type ConversationCreateRequest, - type Message, -} from '../../common/types'; -import type { ChatPromptEditorProps } from '../components/chat/chat_prompt_editor'; -import type { ChatTimelineItem, ChatTimelineProps } from '../components/chat/chat_timeline'; -import { ChatActionClickType } from '../components/chat/types'; -import { EMPTY_CONVERSATION_TITLE } from '../i18n'; -import type { ObservabilityAIAssistantChatService, PendingMessage } from '../types'; -import { - getTimelineItemsfromConversation, - StartedFrom, -} from '../utils/get_timeline_items_from_conversation'; -import type { UseGenAIConnectorsResult } from './use_genai_connectors'; -import { useKibana } from './use_kibana'; - -export function createNewConversation({ - contexts, -}: { - contexts: ContextDefinition[]; -}): ConversationCreateRequest { - return { - '@timestamp': new Date().toISOString(), - messages: [], - conversation: { - title: EMPTY_CONVERSATION_TITLE, - }, - labels: {}, - numeric_labels: {}, - public: false, - }; -} - -export type UseTimelineResult = Pick< - ChatTimelineProps, - 'onEdit' | 'onFeedback' | 'onRegenerate' | 'onStopGenerating' | 'onActionClick' | 'items' -> & - Pick; - -export function useTimeline({ - messages, - connectors, - conversationId, - currentUser, - chatService, - startedFrom, - onChatUpdate, - onChatComplete, -}: { - messages: Message[]; - conversationId?: string; - connectors: UseGenAIConnectorsResult; - currentUser?: Pick; - chatService: ObservabilityAIAssistantChatService; - startedFrom?: StartedFrom; - onChatUpdate: (messages: Message[]) => void; - onChatComplete: (messages: Message[]) => void; -}): UseTimelineResult { - const connectorId = connectors.selectedConnector; - - const hasConnector = !!connectorId; - - const { - services: { notifications }, - } = useKibana(); - - const conversationItems = useMemo(() => { - const items = getTimelineItemsfromConversation({ - currentUser, - chatService, - hasConnector, - messages, - startedFrom, - }); - - return items; - }, [currentUser, chatService, hasConnector, messages, startedFrom]); - - const [subscription, setSubscription] = useState(); - - const controllerRef = useRef(new AbortController()); - - const [pendingMessage, setPendingMessage] = useState(); - - const [isFunctionLoading, setIsFunctionLoading] = useState(false); - - const prevConversationId = usePrevious(conversationId); - - useEffect(() => { - if (prevConversationId !== conversationId && pendingMessage?.error) { - setPendingMessage(undefined); - } - }, [conversationId, pendingMessage?.error, prevConversationId]); - - function chat( - nextMessages: Message[], - response$: Observable | undefined = undefined - ): Promise { - const controller = new AbortController(); - - return new Promise(async (resolve, reject) => { - try { - if (!connectorId) { - reject(new Error('Can not add a message without a connector')); - return; - } - - const isStartOfConversation = - nextMessages.some((message) => message.message.role === MessageRole.Assistant) === false; - - if (isStartOfConversation && chatService.hasFunction('recall')) { - nextMessages = nextMessages.concat({ - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'recall', - arguments: JSON.stringify({ queries: [], contexts: [] }), - trigger: MessageRole.User, - }, - }, - }); - } - - onChatUpdate(nextMessages); - const lastMessage = last(nextMessages); - if (lastMessage?.message.function_call?.name) { - // the user has edited a function suggestion, no need to talk to the LLM - resolve(undefined); - return; - } - - response$ = - response$ || - chatService!.chat({ - messages: nextMessages, - connectorId, - }); - let pendingMessageLocal = pendingMessage; - const nextSubscription = response$.subscribe({ - next: (nextPendingMessage) => { - pendingMessageLocal = nextPendingMessage; - setPendingMessage(() => nextPendingMessage); - }, - error: reject, - complete: () => { - const error = pendingMessageLocal?.error; - if (error) { - notifications.toasts.addError(error, { - title: i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', { - defaultMessage: 'Failed to load response from the AI Assistant', - }), - }); - } - resolve(pendingMessageLocal!); - }, - }); - setSubscription(() => { - controllerRef.current = controller; - return nextSubscription; - }); - } catch (error) { - reject(error); - } - }).then(async (reply) => { - if (reply?.error) { - return nextMessages; - } - if (reply?.aborted) { - return nextMessages; - } - - setPendingMessage(undefined); - - const messagesAfterChat = reply - ? nextMessages.concat({ - '@timestamp': new Date().toISOString(), - message: { - ...reply.message, - }, - }) - : nextMessages; - - onChatUpdate(messagesAfterChat); - - const lastMessage = last(messagesAfterChat); - - if (lastMessage?.message.function_call?.name) { - const name = lastMessage.message.function_call.name; - - setIsFunctionLoading(true); - - try { - let message = await chatService!.executeFunction({ - name, - args: lastMessage.message.function_call.arguments, - messages: messagesAfterChat.slice(0, -1), - signal: controller.signal, - connectorId: connectorId!, - }); - - let nextResponse$: Observable | undefined; - - if (isObservable(message)) { - nextResponse$ = message; - message = { content: '', data: '' }; - } - - return await chat( - messagesAfterChat.concat({ - '@timestamp': new Date().toISOString(), - message: { - name, - role: MessageRole.User, - content: JSON.stringify(message.content), - data: JSON.stringify(message.data), - }, - }), - nextResponse$ - ); - } catch (error) { - return await chat( - messagesAfterChat.concat({ - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.User, - name, - content: JSON.stringify({ - message: error.toString(), - error, - }), - }, - }) - ); - } finally { - setIsFunctionLoading(false); - } - } - - return messagesAfterChat; - }); - } - - const itemsWithAddedLoadingStates = useMemo(() => { - // While we're loading we add an empty loading chat item: - if (pendingMessage || isFunctionLoading) { - const nextItems = conversationItems.concat({ - id: '', - actions: { - canCopy: true, - canEdit: false, - canGiveFeedback: false, - canRegenerate: pendingMessage?.aborted || !!pendingMessage?.error, - }, - display: { - collapsed: false, - hide: pendingMessage?.message.role === MessageRole.System, - }, - content: pendingMessage?.message.content, - currentUser, - error: pendingMessage?.error, - function_call: pendingMessage?.message.function_call, - loading: !pendingMessage?.aborted && !pendingMessage?.error, - role: pendingMessage?.message.role || MessageRole.Assistant, - title: '', - }); - - return nextItems; - } - - if (!isFunctionLoading) { - return conversationItems; - } - - return conversationItems.map((item, index) => { - // When we're done loading we remove the placeholder item again - if (index < conversationItems.length - 1) { - return item; - } - return { - ...item, - loading: true, - }; - }); - }, [conversationItems, pendingMessage, currentUser, isFunctionLoading]); - - const items = useMemo(() => { - const consolidatedChatItems: Array = []; - let currentGroup: ChatTimelineItem[] | null = null; - - for (const item of itemsWithAddedLoadingStates) { - if (item.display.hide || !item) continue; - - if (item.display.collapsed) { - if (currentGroup) { - currentGroup.push(item); - } else { - currentGroup = [item]; - consolidatedChatItems.push(currentGroup); - } - } else { - consolidatedChatItems.push(item); - currentGroup = null; - } - } - - return consolidatedChatItems; - }, [itemsWithAddedLoadingStates]); - - useEffect(() => { - return () => { - subscription?.unsubscribe(); - }; - }, [subscription]); - - return { - items, - onEdit: async (item, newMessage) => { - const indexOf = flatten(items).indexOf(item); - const sliced = messages.slice(0, indexOf); - const nextMessages = await chat(sliced.concat(newMessage)); - onChatComplete(nextMessages); - }, - onFeedback: (item, feedback) => {}, - onRegenerate: (item) => { - const indexOf = flatten(items).indexOf(item); - - chat(messages.slice(0, indexOf)).then((nextMessages) => onChatComplete(nextMessages)); - }, - onStopGenerating: () => { - subscription?.unsubscribe(); - setPendingMessage((prevPendingMessage) => ({ - message: { - role: MessageRole.Assistant, - ...prevPendingMessage?.message, - }, - aborted: true, - error: new AbortError(), - })); - setSubscription(undefined); - }, - onSubmit: async (message) => { - const nextMessages = await chat(messages.concat(message)); - onChatComplete(nextMessages); - }, - onActionClick: async (payload) => { - switch (payload.type) { - case ChatActionClickType.executeEsqlQuery: - const nextMessages = await chat( - messages.concat({ - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'execute_query', - arguments: JSON.stringify({ - query: payload.query, - }), - trigger: MessageRole.User, - }, - }, - }) - ); - onChatComplete(nextMessages); - break; - } - }, - }; -} diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx index f4245cb69d9e7..ed0ac18302cc5 100644 --- a/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/routes/config.tsx @@ -31,11 +31,18 @@ const observabilityAIAssistantRoutes = { element: , }, '/conversations/{conversationId}': { - params: t.type({ - path: t.type({ - conversationId: t.string, + params: t.intersection([ + t.type({ + path: t.type({ + conversationId: t.string, + }), }), - }), + t.partial({ + state: t.partial({ + prevConversationKey: t.string, + }), + }), + ]), element: , }, '/conversations': { diff --git a/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx index 5af2e740deb9b..ca8bf82178ff6 100644 --- a/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/routes/conversations/conversation_view.tsx @@ -4,36 +4,34 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo, useState } from 'react'; -import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; import { css } from '@emotion/css'; import { i18n } from '@kbn/i18n'; import { euiThemeVars } from '@kbn/ui-theme'; +import React, { useMemo, useRef, useState } from 'react'; +import usePrevious from 'react-use/lib/usePrevious'; +import { v4 } from 'uuid'; import { ChatBody } from '../../components/chat/chat_body'; import { ConversationList } from '../../components/chat/conversation_list'; import { ObservabilityAIAssistantChatServiceProvider } from '../../context/observability_ai_assistant_chat_service_provider'; import { useAbortableAsync } from '../../hooks/use_abortable_async'; import { useConfirmModal } from '../../hooks/use_confirm_modal'; -import { useConversation } from '../../hooks/use_conversation'; import { useCurrentUser } from '../../hooks/use_current_user'; +import { useForceUpdate } from '../../hooks/use_force_update'; import { useGenAIConnectors } from '../../hooks/use_genai_connectors'; import { useKibana } from '../../hooks/use_kibana'; import { useKnowledgeBase } from '../../hooks/use_knowledge_base'; import { useObservabilityAIAssistant } from '../../hooks/use_observability_ai_assistant'; import { useObservabilityAIAssistantParams } from '../../hooks/use_observability_ai_assistant_params'; import { useObservabilityAIAssistantRouter } from '../../hooks/use_observability_ai_assistant_router'; +import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; import { getConnectorsManagementHref } from '../../utils/get_connectors_management_href'; import { getModelsManagementHref } from '../../utils/get_models_management_href'; -import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; const containerClassName = css` max-width: 100%; `; -const chatBodyContainerClassNameWithError = css` - align-self: center; -`; - const conversationListContainerName = css` min-width: 250px; width: 250px; @@ -80,12 +78,24 @@ export function ConversationView() { const conversationId = 'conversationId' in path ? path.conversationId : undefined; - const { conversation, displayedMessages, setDisplayedMessages, save, saveTitle } = - useConversation({ - conversationId, - chatService: chatService.value, - connectorId: connectors.selectedConnector, - }); + // Regenerate the key only when the id changes, except after + // creating the conversation. Ideally this happens by adding + // state to the current route, but I'm not keen on adding + // the concept of state to the router, due to a mismatch + // between router.link() and router.push(). So, this is a + // pretty gross workaround for persisting a key under some + // conditions. + const chatBodyKeyRef = useRef(v4()); + const keepPreviousKeyRef = useRef(false); + const prevConversationId = usePrevious(conversationId); + + if (conversationId !== prevConversationId && keepPreviousKeyRef.current === false) { + chatBodyKeyRef.current = v4(); + } + + keepPreviousKeyRef.current = false; + + const forceUpdate = useForceUpdate(); const conversations = useAbortableAsync( ({ signal }) => { @@ -111,14 +121,17 @@ export function ConversationView() { ]; }, [conversations.value?.conversations, conversationId, observabilityAIAssistantRouter]); - function navigateToConversation(nextConversationId?: string) { - observabilityAIAssistantRouter.push( - nextConversationId ? '/conversations/{conversationId}' : '/conversations/new', - { - path: { conversationId: nextConversationId }, + function navigateToConversation(nextConversationId?: string, usePrevConversationKey?: boolean) { + if (nextConversationId) { + observabilityAIAssistantRouter.push('/conversations/{conversationId}', { + path: { + conversationId: nextConversationId, + }, query: {}, - } - ); + }); + } else { + observabilityAIAssistantRouter.push('/conversations/new', { path: {}, query: {} }); + } } function handleRefreshConversations() { @@ -136,10 +149,16 @@ export function ConversationView() { error={conversations.error} conversations={displayedConversations} onClickNewChat={() => { - observabilityAIAssistantRouter.push('/conversations/new', { - path: {}, - query: {}, - }); + if (conversationId) { + observabilityAIAssistantRouter.push('/conversations/new', { + path: {}, + query: {}, + }); + } else { + // clear the chat + chatBodyKeyRef.current = v4(); + forceUpdate(); + } }} onClickDeleteConversation={(id) => { confirmDeleteFunction() @@ -194,69 +213,36 @@ export function ConversationView() { /> - - {conversation.error ? ( - + + + + + + ) : null} + {chatService.value && ( + + { + if (!conversationId) { + keepPreviousKeyRef.current = true; + navigateToConversation(conversation.conversation.id); } - )} - iconType="warning" - > - {i18n.translate('xpack.observabilityAiAssistant.couldNotFindConversationContent', { - defaultMessage: - 'Could not find a conversation with id {conversationId}. Make sure the conversation exists and you have access to it.', - values: { conversationId }, - })} - - ) : null} - {!chatService.value ? ( - - - - - - - ) : null} - {conversation.value && chatService.value && !conversation.error ? ( - - { - setDisplayedMessages(messages); - }} - onChatComplete={(messages) => { - save(messages, handleRefreshConversations) - .then((nextConversation) => { - conversations.refresh(); - if (!conversationId && nextConversation?.conversation?.id) { - navigateToConversation(nextConversation.conversation.id); - } - }) - .catch((e) => {}); - }} - onSaveTitle={(title) => { - saveTitle(title, handleRefreshConversations); - }} - /> - - ) : null} - + handleRefreshConversations(); + }} + /> + + )}
    ); diff --git a/x-pack/plugins/observability_ai_assistant/public/service/create_mock_chat_service.ts b/x-pack/plugins/observability_ai_assistant/public/service/create_mock_chat_service.ts new file mode 100644 index 0000000000000..e255aa830467e --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/service/create_mock_chat_service.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import type { ObservabilityAIAssistantChatService } from '../types'; + +type MockedChatService = DeeplyMockedKeys; + +export const createMockChatService = (): MockedChatService => { + const mockChatService: MockedChatService = { + chat: jest.fn(), + executeFunction: jest.fn(), + getContexts: jest.fn().mockReturnValue([{ name: 'core', description: '' }]), + getFunctions: jest.fn().mockReturnValue([]), + hasFunction: jest.fn().mockReturnValue(false), + hasRenderFunction: jest.fn().mockReturnValue(true), + renderFunction: jest.fn(), + }; + return mockChatService; +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts b/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts index ed318397de73c..6f2d1e5c2f090 100644 --- a/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts +++ b/x-pack/plugins/observability_ai_assistant/public/utils/builders.ts @@ -6,106 +6,98 @@ */ import { merge, uniqueId } from 'lodash'; -import { MessageRole, Conversation, FunctionDefinition } from '../../common/types'; -import { ChatTimelineItem } from '../components/chat/chat_timeline'; +import { DeepPartial } from 'utility-types'; +import { MessageRole, Conversation, FunctionDefinition, Message } from '../../common/types'; import { getAssistantSetupMessage } from '../service/get_assistant_setup_message'; -type ChatItemBuildProps = Omit, 'actions' | 'display' | 'currentUser'> & { - actions?: Partial; - display?: Partial; - currentUser?: Partial; -} & Pick; +type BuildMessageProps = DeepPartial & { + message: { + role: MessageRole; + function_call?: { + name: string; + trigger: MessageRole.Assistant | MessageRole.User | MessageRole.Elastic; + }; + }; +}; -export function buildChatItem(params: ChatItemBuildProps): ChatTimelineItem { +export function buildMessage(params: BuildMessageProps): Message { return merge( { - id: uniqueId(), - title: '', - actions: { - canCopy: true, - canEdit: false, - canGiveFeedback: false, - canRegenerate: params.role === MessageRole.Assistant, - }, - display: { - collapsed: false, - hide: false, - }, - currentUser: { - username: 'elastic', - }, - loading: false, + '@timestamp': new Date().toISOString(), }, params ); } -export function buildSystemChatItem(params?: Omit) { - return buildChatItem({ - role: MessageRole.System, - ...params, - }); -} - -export function buildChatInitItem() { - return buildChatItem({ - role: MessageRole.User, - title: 'started a conversation', - actions: { - canEdit: false, - canCopy: true, - canGiveFeedback: false, - canRegenerate: false, - }, - }); -} - -export function buildUserChatItem(params?: Omit) { - return buildChatItem({ - role: MessageRole.User, - content: "What's a function?", - actions: { - canCopy: true, - canEdit: true, - canGiveFeedback: false, - canRegenerate: true, - }, - ...params, - }); +export function buildSystemMessage( + params?: Omit & { + message: DeepPartial>; + } +) { + return buildMessage( + merge({}, params, { + message: { role: MessageRole.System }, + }) + ); } -export function buildAssistantChatItem(params?: Omit) { - return buildChatItem({ - role: MessageRole.Assistant, - content: `In computer programming and mathematics, a function is a fundamental concept that represents a relationship between input values and output values. It takes one or more input values (also known as arguments or parameters) and processes them to produce a result, which is the output of the function. The input values are passed to the function, and the function performs a specific set of operations or calculations on those inputs to produce the desired output. - A function is often defined with a name, which serves as an identifier to call and use the function in the code. It can be thought of as a reusable block of code that can be executed whenever needed, and it helps in organizing code and making it more modular and maintainable.`, - actions: { - canCopy: true, - canEdit: false, - canRegenerate: true, - canGiveFeedback: true, - }, - ...params, - }); +export function buildUserMessage( + params?: Omit & { + message?: DeepPartial>; + } +) { + return buildMessage( + merge( + { + message: { + content: "What's a function?", + }, + }, + params, + { + message: { role: MessageRole.User }, + } + ) + ); } -export function buildFunctionChatItem(params: Omit) { - return buildChatItem({ - role: MessageRole.User, - title: 'executed a function', - function_call: { - name: 'leftpad', - arguments: '{ foo: "bar" }', - trigger: MessageRole.Assistant, - }, - ...params, - }); +export function buildAssistantMessage( + params?: Omit & { + message: DeepPartial>; + } +) { + return buildMessage( + merge( + { + message: { + content: `In computer programming and mathematics, a function is a fundamental concept that represents a relationship between input values and output values. It takes one or more input values (also known as arguments or parameters) and processes them to produce a result, which is the output of the function. The input values are passed to the function, and the function performs a specific set of operations or calculations on those inputs to produce the desired output. + A function is often defined with a name, which serves as an identifier to call and use the function in the code. It can be thought of as a reusable block of code that can be executed whenever needed, and it helps in organizing code and making it more modular and maintainable.`, + }, + }, + params, + { + message: { role: MessageRole.Assistant }, + } + ) + ); } -export function buildTimelineItems() { - return { - items: [buildSystemChatItem(), buildUserChatItem(), buildAssistantChatItem()], - }; +export function buildFunctionResponseMessage( + params?: Omit & { + message: DeepPartial>; + } +) { + return buildUserMessage( + merge( + {}, + { + message: { + name: 'leftpad', + }, + ...params, + } + ) + ); } export function buildConversation(params?: Partial) { diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.test.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.test.tsx new file mode 100644 index 0000000000000..f066b7a9db370 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.test.tsx @@ -0,0 +1,597 @@ +/* + * Copyright 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 { last, pick } from 'lodash'; +import { render } from '@testing-library/react'; +import { Message, MessageRole } from '../../common'; +import { createMockChatService } from '../service/create_mock_chat_service'; +import { getTimelineItemsfromConversation } from './get_timeline_items_from_conversation'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ObservabilityAIAssistantChatServiceProvider } from '../context/observability_ai_assistant_chat_service_provider'; +import { ChatState } from '../hooks/use_chat'; + +const mockChatService = createMockChatService(); + +let items: ReturnType; + +describe('getTimelineItemsFromConversation', () => { + describe('returns an opening message only', () => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + messages: [], + chatState: ChatState.Ready, + }); + + expect(items.length).toBe(1); + expect(items[0].title).toBe('started a conversation'); + }); + + describe('with a start of a conversation', () => { + beforeEach(() => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + currentUser: { + username: 'johndoe', + full_name: 'John Doe', + }, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'User', + }, + }, + ], + }); + }); + it('excludes the system message', () => { + expect(items.length).toBe(2); + expect(items[0].title).toBe('started a conversation'); + }); + + it('includes the rest of the conversation', () => { + expect(items[1].currentUser?.full_name).toEqual('John Doe'); + expect(items[1].content).toEqual('User'); + }); + + it('formats the user message', () => { + expect(pick(items[1], 'title', 'actions', 'display', 'loading')).toEqual({ + title: '', + actions: { + canCopy: true, + canEdit: true, + canGiveFeedback: false, + canRegenerate: false, + }, + display: { + collapsed: false, + hide: false, + }, + loading: false, + }); + }); + }); + + describe('with function calling', () => { + beforeEach(() => { + mockChatService.hasRenderFunction.mockImplementation(() => false); + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + function_call: { + name: 'recall', + arguments: JSON.stringify({ queries: [], contexts: [] }), + trigger: MessageRole.Assistant, + }, + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name: 'recall', + content: JSON.stringify([]), + }, + }, + ], + }); + }); + + it('formats the function request', () => { + expect(pick(items[2], 'actions', 'display', 'loading')).toEqual({ + actions: { + canCopy: true, + canEdit: true, + canGiveFeedback: false, + canRegenerate: true, + }, + display: { + collapsed: true, + hide: false, + }, + loading: false, + }); + + const { container } = render(items[2].title as React.ReactElement, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + expect(container.textContent).toBe('requested the function recall'); + }); + + it('formats the function response', () => { + expect(pick(items[3], 'actions', 'display', 'loading')).toEqual({ + actions: { + canCopy: true, + canEdit: false, + canGiveFeedback: false, + canRegenerate: false, + }, + display: { + collapsed: true, + hide: false, + }, + loading: false, + }); + + const { container } = render(items[3].title as React.ReactElement, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + expect(container.textContent).toBe('executed the function recall'); + }); + }); + describe('with a render function', () => { + beforeEach(() => { + mockChatService.hasRenderFunction.mockImplementation(() => true); + mockChatService.renderFunction.mockImplementation(() => 'Rendered'); + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + function_call: { + name: 'my_render_function', + arguments: JSON.stringify({ foo: 'bar' }), + trigger: MessageRole.Assistant, + }, + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name: 'my_render_function', + content: JSON.stringify([]), + }, + }, + ], + }); + }); + + it('renders a display element', () => { + expect(mockChatService.hasRenderFunction).toHaveBeenCalledWith('my_render_function'); + + expect(pick(items[3], 'actions', 'display')).toEqual({ + actions: { + canCopy: true, + canEdit: false, + canGiveFeedback: false, + canRegenerate: false, + }, + display: { + collapsed: false, + hide: false, + }, + }); + + expect(items[3].element).toBeTruthy(); + + const { container } = render(items[3].element as React.ReactElement, { + wrapper: ({ children }) => ( + + + {children} + + + ), + }); + + expect(mockChatService.renderFunction).toHaveBeenCalledWith( + 'my_render_function', + JSON.stringify({ foo: 'bar' }), + { content: '[]', name: 'my_render_function', role: 'user' } + ); + + expect(container.textContent).toEqual('Rendered'); + }); + }); + + describe('with a function that errors out', () => { + beforeEach(() => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Hello', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + function_call: { + name: 'my_render_function', + arguments: JSON.stringify({ foo: 'bar' }), + trigger: MessageRole.Assistant, + }, + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name: 'my_render_function', + content: JSON.stringify({ + error: { + message: 'An error occurred', + }, + }), + }, + }, + ], + }); + }); + + it('returns a title that reflects a failure to execute the function', () => { + const { container } = render(items[3].title as React.ReactElement, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + expect(container.textContent).toBe('failed to execute the function my_render_function'); + }); + + it('formats the messages correctly', () => { + expect(pick(items[3], 'actions', 'display', 'loading')).toEqual({ + actions: { + canCopy: true, + canEdit: false, + canGiveFeedback: false, + canRegenerate: false, + }, + display: { + collapsed: true, + hide: false, + }, + loading: false, + }); + }); + }); + + describe('with an invalid JSON response', () => { + beforeEach(() => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + currentUser: { + username: 'johndoe', + full_name: 'John Doe', + }, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'my_function', + arguments: JSON.stringify({}), + trigger: MessageRole.User, + }, + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'invalid-json', + name: 'my_function', + }, + }, + ], + }); + }); + + it('sets the invalid json as content', () => { + expect(items[2].content).toBe( + `\`\`\` +{ + "content": "invalid-json" +} +\`\`\`` + ); + }); + }); + + describe('when starting from a contextual insight', () => { + beforeEach(() => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + currentUser: { + username: 'johndoe', + full_name: 'John Doe', + }, + chatState: ChatState.Ready, + startedFrom: 'contextualInsight', + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Test', + }, + }, + ], + }); + }); + + it('hides the first user message', () => { + expect(items[1].display.collapsed).toBe(true); + }); + }); + + describe('with function calling suggested by the user', () => { + beforeEach(() => { + mockChatService.hasRenderFunction.mockImplementation(() => false); + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + chatState: ChatState.Ready, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + function_call: { + name: 'recall', + arguments: JSON.stringify({ queries: [], contexts: [] }), + trigger: MessageRole.User, + }, + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + name: 'recall', + content: JSON.stringify([]), + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: 'Reply from assistant', + }, + }, + ], + }); + }); + + it('formats the function request', () => { + expect(pick(items[1], 'actions', 'display')).toEqual({ + actions: { + canCopy: true, + canRegenerate: false, + canEdit: true, + canGiveFeedback: false, + }, + display: { + collapsed: true, + hide: false, + }, + }); + }); + + it('formats the assistant response', () => { + expect(pick(items[3], 'actions', 'display')).toEqual({ + actions: { + canCopy: true, + canRegenerate: true, + canEdit: false, + canGiveFeedback: false, + }, + display: { + collapsed: false, + hide: false, + }, + }); + }); + }); + + describe('while the chat is loading', () => { + const renderWithLoading = (extraMessages: Message[]) => { + items = getTimelineItemsfromConversation({ + chatService: mockChatService, + hasConnector: true, + chatState: ChatState.Loading, + messages: [ + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.System, + content: 'System', + }, + }, + { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.User, + content: 'Test', + }, + }, + ...extraMessages, + ], + }); + }; + + describe('with a user message last', () => { + beforeEach(() => { + renderWithLoading([]); + }); + + it('adds an assistant message which is loading', () => { + expect(pick(last(items), 'display', 'actions', 'loading', 'role', 'content')).toEqual({ + loading: true, + role: MessageRole.Assistant, + actions: { + canCopy: false, + canRegenerate: false, + canEdit: false, + canGiveFeedback: false, + }, + display: { + collapsed: false, + hide: false, + }, + content: '', + }); + }); + }); + + describe('with a function request as the last message', () => { + beforeEach(() => { + renderWithLoading([ + { + '@timestamp': new Date().toISOString(), + message: { + function_call: { + name: 'my_function_call', + trigger: MessageRole.Assistant, + }, + role: MessageRole.Assistant, + }, + }, + ]); + }); + + it('adds an assistant message which is loading', () => { + expect(pick(last(items), 'display', 'actions', 'loading', 'role', 'content')).toEqual({ + loading: true, + role: MessageRole.Assistant, + actions: { + canCopy: false, + canRegenerate: false, + canEdit: false, + canGiveFeedback: false, + }, + display: { + collapsed: false, + hide: false, + }, + content: '', + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx index 61f2a4fcbd383..3bf54ec628f24 100644 --- a/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/utils/get_timeline_items_from_conversation.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; import { v4 } from 'uuid'; -import { isEmpty, omitBy } from 'lodash'; +import { isEmpty, last, omitBy } from 'lodash'; import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -15,6 +15,15 @@ import { Message, MessageRole } from '../../common'; import type { ChatTimelineItem } from '../components/chat/chat_timeline'; import { RenderFunction } from '../components/render_function'; import type { ObservabilityAIAssistantChatService } from '../types'; +import { ChatState } from '../hooks/use_chat'; + +function safeParse(jsonStr: string) { + try { + return JSON.parse(jsonStr); + } catch (err) { + return jsonStr; + } +} function convertMessageToMarkdownCodeBlock(message: Message['message']) { let value: object; @@ -22,7 +31,7 @@ function convertMessageToMarkdownCodeBlock(message: Message['message']) { if (!message.name) { const name = message.function_call?.name; const args = message.function_call?.arguments - ? JSON.parse(message.function_call.arguments) + ? safeParse(message.function_call.arguments) : undefined; value = { @@ -32,9 +41,9 @@ function convertMessageToMarkdownCodeBlock(message: Message['message']) { } else { const content = message.role !== MessageRole.Assistant && message.content - ? JSON.parse(message.content) + ? safeParse(message.content) : message.content; - const data = message.data ? JSON.parse(message.data) : undefined; + const data = message.data ? safeParse(message.data) : undefined; value = omitBy( { content, @@ -61,26 +70,36 @@ export function getTimelineItemsfromConversation({ hasConnector, messages, startedFrom, + chatState, }: { chatService: ObservabilityAIAssistantChatService; currentUser?: Pick; hasConnector: boolean; messages: Message[]; startedFrom?: StartedFrom; + chatState: ChatState; }): ChatTimelineItem[] { - return [ + const messagesWithoutSystem = messages.filter( + (message) => message.message.role !== MessageRole.System + ); + + const items: ChatTimelineItem[] = [ { id: v4(), actions: { canCopy: false, canEdit: false, canGiveFeedback: false, canRegenerate: false }, display: { collapsed: false, hide: false }, currentUser, loading: false, - role: MessageRole.User, + message: { + '@timestamp': new Date().toISOString(), + message: { role: MessageRole.User }, + }, title: i18n.translate('xpack.observabilityAiAssistant.conversationStartTitle', { defaultMessage: 'started a conversation', }), + role: MessageRole.User, }, - ...messages.map((message, index) => { + ...messagesWithoutSystem.map((message, index) => { const id = v4(); let title: React.ReactNode = ''; @@ -88,8 +107,10 @@ export function getTimelineItemsfromConversation({ let element: React.ReactNode | undefined; const prevFunctionCall = - message.message.name && messages[index - 1] && messages[index - 1].message.function_call - ? messages[index - 1].message.function_call + message.message.name && + messagesWithoutSystem[index - 1] && + messagesWithoutSystem[index - 1].message.function_call + ? messagesWithoutSystem[index - 1].message.function_call : undefined; let role = message.message.function_call?.trigger || message.message.role; @@ -107,10 +128,6 @@ export function getTimelineItemsfromConversation({ }; switch (role) { - case MessageRole.System: - display.hide = true; - break; - case MessageRole.User: actions.canCopy = true; actions.canGiveFeedback = false; @@ -120,16 +137,15 @@ export function getTimelineItemsfromConversation({ // User executed a function: if (message.message.name && prevFunctionCall) { - let parsedContent; + let isError = false; try { - parsedContent = JSON.parse(message.message.content ?? 'null'); + const parsedContent = JSON.parse(message.message.content ?? 'null'); + isError = + parsedContent && typeof parsedContent === 'object' && 'error' in parsedContent; } catch (error) { - parsedContent = message.message.content; + isError = true; } - const isError = - parsedContent && typeof parsedContent === 'object' && 'error' in parsedContent; - title = !isError ? ( el.message.role === MessageRole.User ); @@ -252,7 +268,53 @@ export function getTimelineItemsfromConversation({ currentUser, function_call: message.message.function_call, loading: false, + message, }; }), ]; + + const isLoading = chatState === ChatState.Loading; + + let lastMessage = last(items); + + const isNaturalLanguageOnlyAnswerFromAssistant = + lastMessage?.message.message.role === MessageRole.Assistant && + !lastMessage.message.message.function_call?.name; + + const addLoadingPlaceholder = isLoading && !isNaturalLanguageOnlyAnswerFromAssistant; + + if (addLoadingPlaceholder) { + items.push({ + id: v4(), + actions: { + canCopy: false, + canEdit: false, + canGiveFeedback: false, + canRegenerate: false, + }, + display: { + collapsed: false, + hide: false, + }, + content: '', + currentUser, + loading: chatState === ChatState.Loading, + role: MessageRole.Assistant, + title: '', + message: { + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: '', + }, + }, + }); + lastMessage = last(items); + } + + if (isLoading && lastMessage) { + lastMessage.loading = isLoading; + } + + return items; } diff --git a/x-pack/plugins/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_ai_assistant/tsconfig.json index afdc9a4a89243..93817dcf79196 100644 --- a/x-pack/plugins/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_ai_assistant/tsconfig.json @@ -46,7 +46,8 @@ "@kbn/es-query", "@kbn/rule-registry-plugin", "@kbn/licensing-plugin", - "@kbn/share-plugin" + "@kbn/share-plugin", + "@kbn/utility-types-jest" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_log_explorer/kibana.jsonc b/x-pack/plugins/observability_log_explorer/kibana.jsonc index 72d03b82d3386..92d2ad70c3175 100644 --- a/x-pack/plugins/observability_log_explorer/kibana.jsonc +++ b/x-pack/plugins/observability_log_explorer/kibana.jsonc @@ -19,6 +19,7 @@ "observabilityShared", "share", "kibanaUtils", + "datasetQuality" ], "optionalPlugins": [ "serverless" diff --git a/x-pack/plugins/observability_log_explorer/public/applications/observability_log_explorer.tsx b/x-pack/plugins/observability_log_explorer/public/applications/observability_log_explorer.tsx index a8c6602f9d49f..8a49db7536350 100644 --- a/x-pack/plugins/observability_log_explorer/public/applications/observability_log_explorer.tsx +++ b/x-pack/plugins/observability_log_explorer/public/applications/observability_log_explorer.tsx @@ -10,7 +10,7 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { Route, Router, Routes } from '@kbn/shared-ux-router'; import React from 'react'; import ReactDOM from 'react-dom'; -import { ObservablityLogExplorerMainRoute } from '../routes/main'; +import { DatasetQualityRoute, ObservablityLogExplorerMainRoute } from '../routes/main'; import { ObservabilityLogExplorerAppMountParameters, ObservabilityLogExplorerPluginStart, @@ -72,6 +72,11 @@ export const ObservabilityLogExplorerApp = ({ exact={true} render={() => } /> + } + /> diff --git a/x-pack/plugins/observability_log_explorer/public/components/page_template.tsx b/x-pack/plugins/observability_log_explorer/public/components/page_template.tsx index e79b8b1bc6271..d128c6e8a7779 100644 --- a/x-pack/plugins/observability_log_explorer/public/components/page_template.tsx +++ b/x-pack/plugins/observability_log_explorer/public/components/page_template.tsx @@ -13,10 +13,14 @@ import React from 'react'; export const ObservabilityLogExplorerPageTemplate = ({ children, observabilityShared, + pageProps, }: React.PropsWithChildren<{ observabilityShared: ObservabilitySharedPluginStart; + pageProps?: EuiPageSectionProps; }>) => ( - + {children} ); diff --git a/x-pack/plugins/observability_log_explorer/public/routes/main/dataset_quality_route.tsx b/x-pack/plugins/observability_log_explorer/public/routes/main/dataset_quality_route.tsx new file mode 100644 index 0000000000000..b76a462eba25d --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/public/routes/main/dataset_quality_route.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import React from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { datasetQualityAppTitle } from '@kbn/dataset-quality-plugin/public'; +import { ObservabilityLogExplorerPageTemplate } from '../../components/page_template'; +import { useBreadcrumbs } from '../../utils/breadcrumbs'; +import { useKibanaContextForPlugin } from '../../utils/use_kibana'; + +export interface DatasetQualityRouteProps { + core: CoreStart; +} + +export const DatasetQualityRoute = ({ core }: DatasetQualityRouteProps) => { + const { services } = useKibanaContextForPlugin(); + const { observabilityShared, serverless, datasetQuality: DatasetQuality } = services; + const breadcrumb: EuiBreadcrumb[] = [ + { + text: datasetQualityAppTitle, + }, + ]; + + useBreadcrumbs(breadcrumb, core.chrome, serverless); + + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/observability_log_explorer/public/routes/main/index.tsx b/x-pack/plugins/observability_log_explorer/public/routes/main/index.tsx index 889e340497cf9..9d755f302d162 100644 --- a/x-pack/plugins/observability_log_explorer/public/routes/main/index.tsx +++ b/x-pack/plugins/observability_log_explorer/public/routes/main/index.tsx @@ -6,3 +6,4 @@ */ export * from './main_route'; +export * from './dataset_quality_route'; diff --git a/x-pack/plugins/observability_log_explorer/public/types.ts b/x-pack/plugins/observability_log_explorer/public/types.ts index 8b315ad206ce4..5f455088b7442 100644 --- a/x-pack/plugins/observability_log_explorer/public/types.ts +++ b/x-pack/plugins/observability_log_explorer/public/types.ts @@ -13,6 +13,7 @@ import { ServerlessPluginStart } from '@kbn/serverless/public'; import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import { AppMountParameters, ScopedHistory } from '@kbn/core/public'; import { LogsSharedClientStartExports } from '@kbn/logs-shared-plugin/public'; +import { DatasetQualityPluginStart } from '@kbn/dataset-quality-plugin/public'; import { ObservabilityLogExplorerLocators, ObservabilityLogExplorerLocationState, @@ -38,6 +39,7 @@ export interface ObservabilityLogExplorerStartDeps { observabilityShared: ObservabilitySharedPluginStart; serverless?: ServerlessPluginStart; share: SharePluginStart; + datasetQuality: DatasetQualityPluginStart; } export type ObservabilityLogExplorerHistory = ScopedHistory; diff --git a/x-pack/plugins/observability_log_explorer/tsconfig.json b/x-pack/plugins/observability_log_explorer/tsconfig.json index 109b54b929ec7..24327c31c26a3 100644 --- a/x-pack/plugins/observability_log_explorer/tsconfig.json +++ b/x-pack/plugins/observability_log_explorer/tsconfig.json @@ -34,7 +34,8 @@ "@kbn/xstate-utils", "@kbn/shared-ux-utility", "@kbn/ui-theme", - "@kbn/logs-shared-plugin" + "@kbn/logs-shared-plugin", + "@kbn/dataset-quality-plugin" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx new file mode 100644 index 0000000000000..02908cdf67a0c --- /dev/null +++ b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { default as React, useEffect, useRef, useState } from 'react'; +import { EMBEDDABLE_PROFILING_SEARCH_BAR } from '.'; +import { ObservabilitySharedStart } from '../../../plugin'; + +export interface EmbeddableProfilingSearchBarProps { + kuery: string; + showDatePicker?: boolean; + onQuerySubmit: (params: { + dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; + query: string; + }) => void; + onRefresh: () => void; + rangeFrom: string; + rangeTo: string; +} + +export function EmbeddableProfilingSearchBar(props: EmbeddableProfilingSearchBarProps) { + const { embeddable: embeddablePlugin } = useKibana().services; + const [embeddable, setEmbeddable] = useState(); + const embeddableRoot: React.RefObject = useRef(null); + + useEffect(() => { + async function createEmbeddable() { + const factory = embeddablePlugin?.getEmbeddableFactory(EMBEDDABLE_PROFILING_SEARCH_BAR); + const input = { + id: 'embeddable_profiling', + }; + const embeddableObject = await factory?.create(input); + setEmbeddable(embeddableObject); + } + createEmbeddable(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (embeddableRoot.current && embeddable) { + embeddable.render(embeddableRoot.current); + } + }, [embeddable, embeddableRoot]); + + useEffect(() => { + if (embeddable) { + embeddable.updateInput({ + ...props, + }); + embeddable.reload(); + } + }, [embeddable, props]); + + return ( +
    + ); +} diff --git a/x-pack/plugins/observability_shared/public/components/profiling/embeddables/index.ts b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/index.ts index 2e346d55c835a..05556b839cff4 100644 --- a/x-pack/plugins/observability_shared/public/components/profiling/embeddables/index.ts +++ b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/index.ts @@ -14,3 +14,11 @@ export { EmbeddableFlamegraph } from './embeddable_flamegraph'; export const EMBEDDABLE_FUNCTIONS = 'EMBEDDABLE_FUNCTIONS'; /** Profiling functions embeddable */ export { EmbeddableFunctions } from './embeddable_functions'; + +/** Profiling search bar embeddable key */ +export const EMBEDDABLE_PROFILING_SEARCH_BAR = 'EMBEDDABLE_PROFILING_SEARCH_BAR'; +/** Profiling search bar embeddable */ +export { + EmbeddableProfilingSearchBar, + type EmbeddableProfilingSearchBarProps, +} from './embeddable_profiling_search_bar'; diff --git a/x-pack/plugins/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx index 1a703d11ed6c9..00f29c10c1593 100644 --- a/x-pack/plugins/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx +++ b/x-pack/plugins/observability_shared/public/components/profiling/embeddables/profiling_embeddable.tsx @@ -31,7 +31,11 @@ export function ProfilingEmbeddable({ useEffect(() => { async function createEmbeddable() { const factory = embeddablePlugin?.getEmbeddableFactory(embeddableFactoryId); - const input = { id: 'embeddable_profiling', data, isLoading }; + const input = { + id: 'embeddable_profiling', + data, + isLoading, + }; const embeddableObject = await factory?.create(input); setEmbeddable(embeddableObject); } @@ -47,7 +51,11 @@ export function ProfilingEmbeddable({ useEffect(() => { if (embeddable) { - embeddable.updateInput({ data, isLoading, ...props }); + embeddable.updateInput({ + data, + isLoading, + ...props, + }); embeddable.reload(); } }, [data, embeddable, isLoading, props]); diff --git a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx b/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx deleted file mode 100644 index 21c6a08815b76..0000000000000 --- a/x-pack/plugins/observability_shared/public/hooks/use_get_user_cases_permissions.tsx +++ /dev/null @@ -1,52 +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 { useEffect, useState } from 'react'; -import { CasesPermissions } from '@kbn/cases-plugin/common'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { casesFeatureId } from '../../common'; -import { ObservabilitySharedStart } from '../plugin'; - -export function useGetUserCasesPermissions() { - const [casesPermissions, setCasesPermissions] = useState({ - all: false, - read: false, - create: false, - update: false, - delete: false, - push: false, - connectors: false, - }); - const uiCapabilities = useKibana().services.application!.capabilities; - - const casesCapabilities = - useKibana().services.cases.helpers.getUICapabilities( - uiCapabilities[casesFeatureId] - ); - - useEffect(() => { - setCasesPermissions({ - all: casesCapabilities.all, - create: casesCapabilities.create, - read: casesCapabilities.read, - update: casesCapabilities.update, - delete: casesCapabilities.delete, - push: casesCapabilities.push, - connectors: casesCapabilities.connectors, - }); - }, [ - casesCapabilities.all, - casesCapabilities.create, - casesCapabilities.read, - casesCapabilities.update, - casesCapabilities.delete, - casesCapabilities.push, - casesCapabilities.connectors, - ]); - - return casesPermissions; -} diff --git a/x-pack/plugins/observability_shared/public/index.ts b/x-pack/plugins/observability_shared/public/index.ts index 66492328e4b89..8d8556e509e25 100644 --- a/x-pack/plugins/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_shared/public/index.ts @@ -57,7 +57,6 @@ export { } from './hooks/use_track_metric'; export type { TrackEvent } from './hooks/use_track_metric'; export { useQuickTimeRanges } from './hooks/use_quick_time_ranges'; -export { useGetUserCasesPermissions } from './hooks/use_get_user_cases_permissions'; export { useTimeZone } from './hooks/use_time_zone'; export { useChartTheme } from './hooks/use_chart_theme'; export { useLinkProps, shouldHandleLinkEvent } from './hooks/use_link_props'; @@ -66,7 +65,7 @@ export { NavigationWarningPromptProvider, Prompt } from './components/navigation export type { ApmIndicesConfig, UXMetrics } from './types'; -export { noCasesPermissions } from './utils/cases_permissions'; +export { noCasesPermissions, allCasesPermissions } from './utils/cases_permissions'; export { type ObservabilityActionContextMenuItemProps, @@ -83,6 +82,9 @@ export { export { EMBEDDABLE_FLAMEGRAPH, EMBEDDABLE_FUNCTIONS, + EMBEDDABLE_PROFILING_SEARCH_BAR, EmbeddableFlamegraph, EmbeddableFunctions, + EmbeddableProfilingSearchBar, + type EmbeddableProfilingSearchBarProps, } from './components/profiling/embeddables'; diff --git a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts index a0b6a8aed95b0..0ceea46ad0d38 100644 --- a/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts +++ b/x-pack/plugins/observability_shared/public/utils/cases_permissions.ts @@ -13,4 +13,16 @@ export const noCasesPermissions = () => ({ delete: false, push: false, connectors: false, + settings: false, +}); + +export const allCasesPermissions = () => ({ + all: true, + create: true, + read: true, + update: true, + delete: true, + push: true, + connectors: true, + settings: true, }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts index 7f6f0a006e05b..d9cbe5c69ed6c 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_cases.cy.ts @@ -65,10 +65,12 @@ describe('Alert Event Details - Cases', { tags: ['@ess', '@serverless'] }, () => it('runs osquery against alert and creates a new case', () => { const [caseName, caseDescription] = generateRandomStringName(2); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.getBySel('take-action-dropdown-btn').click(); cy.getBySel('osquery-action-item').click(); cy.contains(/^\d+ agen(t|ts) selected/); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.wait(1000); cy.contains('Run a set of queries in a pack').click(); cy.get(OSQUERY_FLYOUT_BODY_EDITOR).should('not.exist'); cy.getBySel('globalLoadingIndicator').should('not.exist'); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts index cb4ce27a69031..f0d3c3468f16c 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_response_actions_form.cy.ts @@ -15,6 +15,7 @@ import { packFixture, } from '../../tasks/api_fixtures'; import { + RESPONSE_ACTIONS_ERRORS, OSQUERY_RESPONSE_ACTION_ADD_BUTTON, RESPONSE_ACTIONS_ITEM_0, RESPONSE_ACTIONS_ITEM_1, @@ -66,6 +67,48 @@ describe('Alert Event Details - Response Actions Form', { tags: ['@ess', '@serve cy.getBySel('globalLoadingIndicator').should('not.exist'); cy.contains('Response actions are run on each rule execution.'); cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); + + cy.getBySel(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains('Query is a required field'); + cy.contains('Timeout value must be greater than 60 seconds.').should('not.exist'); + }); + + // check if changing error state of one input doesn't clear other errors - START + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.contains('Advanced').click(); + cy.getBySel('timeout-input').clear(); + cy.contains('Timeout value must be greater than 60 seconds.'); + }); + + cy.getBySel(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains('Query is a required field'); + cy.contains('Timeout value must be greater than 60 seconds.'); + }); + + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.getBySel('timeout-input').type('6'); + cy.contains('Timeout value must be greater than 60 seconds.'); + }); + cy.getBySel(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains('Query is a required field'); + cy.contains('Timeout value must be greater than 60 seconds.'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.getBySel('timeout-input').type('6'); + cy.contains('Timeout value must be greater than 60 seconds.').should('not.exist'); + }); + cy.getBySel(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains('Query is a required field'); + }); + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { + cy.getBySel('timeout-input').type('6'); + }); + cy.getBySel(RESPONSE_ACTIONS_ERRORS).within(() => { + cy.contains('Query is a required field'); + cy.contains('Timeout value must be greater than 60 seconds.').should('not.exist'); + }); + // check if changing error state of one input doesn't clear other errors - END + cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { cy.contains('Query is a required field'); inputQuery('select * from uptime1'); @@ -74,7 +117,7 @@ describe('Alert Event Details - Response Actions Form', { tags: ['@ess', '@serve cy.getBySel(RESPONSE_ACTIONS_ITEM_1).within(() => { cy.contains('Run a set of queries in a pack').click(); }); - cy.getBySel('response-actions-error') + cy.getBySel(RESPONSE_ACTIONS_ERRORS) .within(() => { cy.contains('Pack is a required field'); }) 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 fc79d1b9a69bb..04a3a24fbabb5 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 @@ -37,10 +37,10 @@ describe('ALL - Live Query', { tags: ['@ess', '@serverless'] }, () => { cy.contains('Query is a required field').should('not.exist'); checkResults(); getAdvancedButton().click(); - fillInQueryTimeout('91'); + fillInQueryTimeout('910'); submitQuery(); cy.contains('Timeout value must be lower than 900 seconds.'); - fillInQueryTimeout('89'); + fillInQueryTimeout('890'); submitQuery(); cy.contains('Timeout value must be lower than 900 seconds.').should('not.exist'); typeInOsqueryFieldInput('days{downArrow}{enter}'); diff --git a/x-pack/plugins/osquery/cypress/tasks/response_actions.ts b/x-pack/plugins/osquery/cypress/tasks/response_actions.ts index d686392431b7a..8f0b5638bdcb0 100644 --- a/x-pack/plugins/osquery/cypress/tasks/response_actions.ts +++ b/x-pack/plugins/osquery/cypress/tasks/response_actions.ts @@ -10,6 +10,7 @@ import { ServerlessRoleName } from '../support/roles'; import { cleanupRule, loadRule } from './api_fixtures'; import { closeDateTabIfVisible } from './integrations'; +export const RESPONSE_ACTIONS_ERRORS = 'response-actions-error'; export const RESPONSE_ACTIONS_ITEM_0 = 'response-actions-list-item-0'; export const RESPONSE_ACTIONS_ITEM_1 = 'response-actions-list-item-1'; export const RESPONSE_ACTIONS_ITEM_2 = 'response-actions-list-item-2'; diff --git a/x-pack/plugins/osquery/public/form/timeout_field.tsx b/x-pack/plugins/osquery/public/form/timeout_field.tsx index 5ef6627ba2c43..cdbc415a69c8f 100644 --- a/x-pack/plugins/osquery/public/form/timeout_field.tsx +++ b/x-pack/plugins/osquery/public/form/timeout_field.tsx @@ -7,30 +7,12 @@ import React, { useCallback, useMemo } from 'react'; import deepEqual from 'fast-deep-equal'; import { useController } from 'react-hook-form'; -import type { EuiFieldNumberProps } from '@elastic/eui'; import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { QUERY_TIMEOUT } from '../../common/constants'; -const timeoutFieldValidations = { - min: { - message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.timeoutFieldMinNumberError', { - defaultMessage: 'Timeout value must be greater than {than} seconds.', - values: { than: QUERY_TIMEOUT.DEFAULT }, - }), - value: QUERY_TIMEOUT.DEFAULT, - }, - max: { - message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.timeoutFieldMaxNumberError', { - defaultMessage: 'Timeout value must be lower than {than} seconds.', - values: { than: QUERY_TIMEOUT.MAX }, - }), - value: QUERY_TIMEOUT.MAX, - }, -}; - interface TimeoutFieldProps { euiFieldProps?: Record; } @@ -43,12 +25,26 @@ const TimeoutFieldComponent = ({ euiFieldProps }: TimeoutFieldProps) => { name: 'timeout', defaultValue: QUERY_TIMEOUT.DEFAULT, rules: { - ...timeoutFieldValidations, + validate: (currentValue: number) => { + if (currentValue < QUERY_TIMEOUT.DEFAULT || isNaN(currentValue)) { + return i18n.translate('xpack.osquery.pack.queryFlyoutForm.timeoutFieldMinNumberError', { + defaultMessage: 'Timeout value must be greater than {than} seconds.', + values: { than: QUERY_TIMEOUT.DEFAULT }, + }); + } + + if (currentValue > QUERY_TIMEOUT.MAX) { + return i18n.translate('xpack.osquery.pack.queryFlyoutForm.timeoutFieldMaxNumberError', { + defaultMessage: 'Timeout value must be lower than {than} seconds.', + values: { than: QUERY_TIMEOUT.MAX }, + }); + } + }, }, }); const handleChange = useCallback( (e: React.ChangeEvent) => { - const numberValue = e.target.valueAsNumber ? e.target.valueAsNumber : 0; + const numberValue = parseInt(e.target.value, 10); onChange(numberValue); }, [onChange] @@ -77,7 +73,7 @@ const TimeoutFieldComponent = ({ euiFieldProps }: TimeoutFieldProps) => { > = ({ - + 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 79e0cdb2d2a48..42612b25a673d 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 @@ -46,15 +46,20 @@ export const createActionHandler = async ( const { soClient, metadata, alertData, error } = options; const savedObjectsClient = soClient ?? coreStartServices.savedObjects.createInternalRepository(); - + const elasticsearchClient = coreStartServices.elasticsearch.client.asInternalUser; // eslint-disable-next-line @typescript-eslint/naming-convention const { agent_all, agent_ids, agent_platforms, agent_policy_ids } = params; - const selectedAgents = await parseAgentSelection(internalSavedObjectsClient, osqueryContext, { - agents: agent_ids, - allAgentsSelected: !!agent_all, - platformsSelected: agent_platforms, - policiesSelected: agent_policy_ids, - }); + const selectedAgents = await parseAgentSelection( + internalSavedObjectsClient, + elasticsearchClient, + osqueryContext, + { + agents: agent_ids, + allAgentsSelected: !!agent_all, + platformsSelected: agent_platforms, + policiesSelected: agent_policy_ids, + } + ); if (!selectedAgents.length) { throw new CustomHttpRequestError('No agents found for selection', 400); diff --git a/x-pack/plugins/osquery/server/lib/parse_agent_groups.test.ts b/x-pack/plugins/osquery/server/lib/parse_agent_groups.test.ts new file mode 100644 index 0000000000000..7728028a3eca7 --- /dev/null +++ b/x-pack/plugins/osquery/server/lib/parse_agent_groups.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { aggregateResults } from './parse_agent_groups'; +import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import type { OsqueryAppContext } from './osquery_app_context_services'; + +const mockOpenPointInTime = jest.fn().mockResolvedValue({ id: 'mockedPitId' }); +const mockClosePointInTime = jest.fn(); + +const mockElasticsearchClient = { + openPointInTime: mockOpenPointInTime, + closePointInTime: mockClosePointInTime, +} as unknown as ElasticsearchClientMock; + +const mockContext = {} as unknown as OsqueryAppContext; + +describe('aggregateResults', () => { + it('should handle one page of results', async () => { + const generatorMock = jest.fn().mockResolvedValue({ + results: ['result1', 'result2'], + total: 2, + }); + + const result = await aggregateResults(generatorMock, mockElasticsearchClient, mockContext); + + expect(generatorMock).toHaveBeenCalledWith(1, expect.any(Number)); // 1st page, PER_PAGE + expect(mockOpenPointInTime).not.toHaveBeenCalled(); + expect(mockClosePointInTime).not.toHaveBeenCalled(); + + expect(result).toEqual(['result1', 'result2']); + }); + + it('should handle multiple pages of results', async () => { + const generateResults = (run = 1, length = 9000) => + Array.from({ length }, (_, index) => `result_${index + 1 + (run - 1) * length}`); + + const generatorMock = jest + .fn() + .mockResolvedValueOnce({ + results: generateResults(), + total: 18001, + }) + .mockResolvedValueOnce({ + results: generateResults(), + total: 18001, + searchAfter: ['firstSort'], + }) + .mockResolvedValueOnce({ + results: generateResults(2), + total: 18001, + searchAfter: ['secondSort'], + }) + .mockResolvedValueOnce({ + results: ['result_18001'], + total: 18001, + searchAfter: ['thirdSort'], + }); + + const result = await aggregateResults(generatorMock, mockElasticsearchClient, mockContext); + expect(generatorMock).toHaveBeenCalledWith(1, expect.any(Number)); + expect(generatorMock).toHaveBeenCalledWith(1, expect.any(Number), undefined, 'mockedPitId'); + expect(generatorMock).toHaveBeenCalledWith(2, expect.any(Number), ['firstSort'], 'mockedPitId'); + expect(generatorMock).toHaveBeenCalledWith( + 3, + expect.any(Number), + ['secondSort'], + 'mockedPitId' + ); + expect(mockOpenPointInTime).toHaveBeenCalledTimes(1); + expect(mockClosePointInTime).toHaveBeenCalledTimes(1); + expect(mockClosePointInTime).toHaveBeenCalledWith({ id: 'mockedPitId' }); + expect(result.length).toEqual(18001); + }); +}); diff --git a/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts b/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts index 723216a675fb4..642bfd4adfa2c 100644 --- a/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts +++ b/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts @@ -6,8 +6,8 @@ */ import { uniq } from 'lodash'; -import type { SavedObjectsClientContract } from '@kbn/core/server'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; +import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import { AGENTS_INDEX, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import { OSQUERY_INTEGRATION_NAME } from '../../common'; import type { OsqueryAppContext } from './osquery_app_context_services'; @@ -20,15 +20,56 @@ export interface AgentSelection { const PER_PAGE = 9000; -const aggregateResults = async ( - generator: (page: number, perPage: number) => Promise<{ results: string[]; total: number }> +export const aggregateResults = async ( + generator: ( + page: number, + perPage: number, + searchAfter?: unknown[], + pitId?: string + ) => Promise<{ results: string[]; total: number; searchAfter?: unknown[] }>, + esClient: ElasticsearchClient, + context: OsqueryAppContext ) => { - const { results, total } = await generator(1, PER_PAGE); + let results: string[]; + const { results: initialResults, total } = await generator(1, PER_PAGE); const totalPages = Math.ceil(total / PER_PAGE); - let currPage = 2; - while (currPage <= totalPages) { - const { results: additionalResults } = await generator(currPage++, PER_PAGE); - results.push(...additionalResults); + if (totalPages === 1) { + // One page only, no need for PIT + results = initialResults; + } else { + const { id: pitId } = await esClient.openPointInTime({ + index: AGENTS_INDEX, + keep_alive: '10m', + }); + let currentSort: unknown[] | undefined; + // Refetch first page with PIT + const { results: pitInitialResults, searchAfter } = await generator( + 1, + PER_PAGE, + currentSort, // No searchAfter for first page, its built based on first page results + pitId + ); + results = pitInitialResults; + currentSort = searchAfter; + let currPage = 2; + while (currPage <= totalPages) { + const { results: additionalResults, searchAfter: additionalSearchAfter } = await generator( + currPage++, + PER_PAGE, + currentSort, + pitId + ); + results.push(...additionalResults); + currentSort = additionalSearchAfter; + } + + try { + await esClient.closePointInTime({ id: pitId }); + } catch (error) { + context.logFactory + .get() + .warn(`Error closing point in time with id: ${pitId}. Error: ${error.message}`); + } } return uniq(results); @@ -36,6 +77,7 @@ const aggregateResults = async ( export const parseAgentSelection = async ( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, context: OsqueryAppContext, agentSelection: AgentSelection ) => { @@ -52,28 +94,42 @@ export const parseAgentSelection = async ( const kueryFragments = ['status:online']; if (agentService && packagePolicyService) { - const osqueryPolicies = await aggregateResults(async (page, perPage) => { - const { items, total } = await packagePolicyService.list(soClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage, - page, - }); + const osqueryPolicies = await aggregateResults( + async (page, perPage) => { + const { items, total } = await packagePolicyService.list(soClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage, + page, + }); - return { results: items.map((it) => it.policy_id), total }; - }); + return { results: items.map((it) => it.policy_id), total }; + }, + esClient, + context + ); kueryFragments.push(`policy_id:(${uniq(osqueryPolicies).join(' or ')})`); if (allAgentsSelected) { const kuery = kueryFragments.join(' and '); - const fetchedAgents = await aggregateResults(async (page, perPage) => { - const res = await agentService.listAgents({ - perPage, - page, - kuery, - showInactive: false, - }); + const fetchedAgents = await aggregateResults( + async (page, perPage, searchAfter?: unknown[], pitId?: string) => { + const res = await agentService.listAgents({ + ...(searchAfter ? { searchAfter } : {}), + ...(pitId ? { pitId } : {}), + perPage, + page, + kuery, + showInactive: false, + }); - return { results: res.agents.map((agent) => agent.id), total: res.total }; - }); + return { + results: res.agents.map((agent) => agent.id), + total: res.total, + searchAfter: res.agents[res.agents.length - 1].sort, + }; + }, + esClient, + context + ); fetchedAgents.forEach(addAgent); } else { if (platformsSelected.length > 0 || policiesSelected.length > 0) { @@ -88,16 +144,26 @@ export const parseAgentSelection = async ( kueryFragments.push(`(${groupFragments.join(' or ')})`); const kuery = kueryFragments.join(' and '); - const fetchedAgents = await aggregateResults(async (page, perPage) => { - const res = await agentService.listAgents({ - perPage, - page, - kuery, - showInactive: false, - }); + const fetchedAgents = await aggregateResults( + async (page, perPage, searchAfter?: unknown[], pitId?: string) => { + const res = await agentService.listAgents({ + ...(searchAfter ? { searchAfter } : {}), + ...(pitId ? { pitId } : {}), + perPage, + page, + kuery, + showInactive: false, + }); - return { results: res.agents.map((agent) => agent.id), total: res.total }; - }); + return { + results: res.agents.map((agent) => agent.id), + total: res.total, + searchAfter: res.agents[res.agents.length - 1].sort, + }; + }, + esClient, + context + ); fetchedAgents.forEach(addAgent); } } diff --git a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/differential_functions.cy.ts b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/differential_functions.cy.ts index bbbcb6c8abdcc..d999374a68bdb 100644 --- a/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/differential_functions.cy.ts +++ b/x-pack/plugins/profiling/e2e/cypress/e2e/profiling_views/differential_functions.cy.ts @@ -167,17 +167,8 @@ describe('Differential Functions page', () => { }); cy.wait('@getTopNFunctions'); cy.wait('@getTopNFunctions'); - cy.get('[data-test-subj="topNFunctionsGrid"] .euiDataGridRow').should('have.length.gt', 1); - cy.get('[data-test-subj="TopNFunctionsComparisonGrid"] .euiDataGridRow').should( - 'have.length.gt', - 1 - ); - cy.get( - '[data-test-subj="topNFunctionsGrid"] [data-test-subj="profilingStackFrameSummaryLink"]' - ).contains('vmlinux'); - cy.get( - '[data-test-subj="TopNFunctionsComparisonGrid"] [data-test-subj="profilingStackFrameSummaryLink"]' - ).contains('vmlinux'); + cy.get('[data-test-subj="frame"]').contains('vmlinux'); + cy.get('[data-test-subj="comparison_frame"]').contains('vmlinux'); cy.addKqlFilter({ key: 'process.thread.name', @@ -190,12 +181,8 @@ describe('Differential Functions page', () => { }); cy.wait('@getTopNFunctions'); cy.wait('@getTopNFunctions'); - cy.get( - '[data-test-subj="topNFunctionsGrid"] [data-test-subj="profilingStackFrameSummaryLink"]' - ).contains('libsystemd-shared-237.so'); - cy.get( - '[data-test-subj="TopNFunctionsComparisonGrid"] [data-test-subj="profilingStackFrameSummaryLink"]' - ).contains('libjvm.so'); + cy.get('[data-test-subj="frame"]').contains('libsystemd-shared-237.so'); + cy.get('[data-test-subj="comparison_frame"]').contains('libjvm.so'); }); }); }); diff --git a/x-pack/plugins/profiling/public/components/contexts/profiling_dependencies/mock_profiling_dependencies_storybook.tsx b/x-pack/plugins/profiling/public/components/contexts/profiling_dependencies/mock_profiling_dependencies_storybook.tsx index 9e4ebc0afeae8..3508f3f090c6a 100644 --- a/x-pack/plugins/profiling/public/components/contexts/profiling_dependencies/mock_profiling_dependencies_storybook.tsx +++ b/x-pack/plugins/profiling/public/components/contexts/profiling_dependencies/mock_profiling_dependencies_storybook.tsx @@ -43,6 +43,7 @@ const mockPlugin = { }; const mockCore = { + uiSettings: { get: () => {} }, application: { currentAppId$: new Observable(), getUrlForApp: (appId: string) => '', diff --git a/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_columns.tsx b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_columns.tsx new file mode 100644 index 0000000000000..71ead9cd75a85 --- /dev/null +++ b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_columns.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiDataGridColumn, EuiDataGridColumnCellAction } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { TopNComparisonFunctionSortField, TopNFunctionSortField } from '@kbn/profiling-utils'; +import React from 'react'; +import { CPULabelWithHint } from '../cpu_label_with_hint'; +import { LabelWithHint } from '../label_with_hint'; + +export const getColumns = ( + compareFrameAction: EuiDataGridColumnCellAction +): EuiDataGridColumn[] => [ + { + id: TopNFunctionSortField.Rank, + actions: { showHide: false }, + displayAsText: 'Rank', + initialWidth: 65, + schema: 'numeric', + }, + { + id: TopNFunctionSortField.Frame, + actions: { showHide: false }, + displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', { + defaultMessage: 'Function', + }), + cellActions: [compareFrameAction], + }, + { + id: TopNFunctionSortField.Samples, + initialWidth: 120, + schema: 'numeric', + actions: { showHide: false }, + display: ( + + ), + }, + { + id: TopNFunctionSortField.SelfCPU, + actions: { showHide: false }, + schema: 'numeric', + initialWidth: 120, + display: ( + + ), + }, + { + id: TopNFunctionSortField.TotalCPU, + actions: { showHide: false }, + schema: 'numeric', + initialWidth: 120, + display: ( + + ), + }, + { + id: TopNComparisonFunctionSortField.ComparisonRank, + actions: { showHide: false }, + schema: 'numeric', + displayAsText: 'Rank', + initialWidth: 69, + displayHeaderCellProps: { className: 'thickBorderLeft' }, + }, + { + id: TopNComparisonFunctionSortField.ComparisonFrame, + actions: { showHide: false }, + displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', { + defaultMessage: 'Function', + }), + cellActions: [compareFrameAction], + }, + { + id: TopNComparisonFunctionSortField.ComparisonSamples, + actions: { showHide: false }, + schema: 'numeric', + initialWidth: 120, + display: ( + + ), + }, + { + id: TopNComparisonFunctionSortField.ComparisonSelfCPU, + actions: { showHide: false }, + schema: 'numeric', + initialWidth: 120, + display: ( + + ), + }, + { + id: TopNComparisonFunctionSortField.ComparisonTotalCPU, + actions: { showHide: false }, + schema: 'numeric', + initialWidth: 120, + display: ( + + ), + }, + { + displayAsText: 'Diff', + actions: { showHide: false }, + id: TopNComparisonFunctionSortField.ComparisonDiff, + initialWidth: 70, + isSortable: false, + }, +]; diff --git a/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_compare_frame_action.tsx b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_compare_frame_action.tsx new file mode 100644 index 0000000000000..32f6bc826d986 --- /dev/null +++ b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/get_compare_frame_action.tsx @@ -0,0 +1,121 @@ +/* + * Copyright 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 { + EuiBasicTable, + EuiDataGridColumnCellAction, + EuiDataGridColumnCellActionProps, + EuiPopover, + EuiPopoverTitle, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { getCalleeFunction } from '@kbn/profiling-utils'; +import { getFrameIdentification, isComparisonColumn, SelectedFrame } from '.'; +import { IFunctionRow } from '../topn_functions/utils'; + +interface Props { + baseRows: IFunctionRow[]; + comparisonRows: IFunctionRow[]; + selectedFrame?: SelectedFrame; + onClick: (selectedFrame?: SelectedFrame) => void; +} + +export const getCompareFrameAction = + ({ baseRows, comparisonRows, selectedFrame, onClick }: Props): EuiDataGridColumnCellAction => + ({ rowIndex, columnId, Component }: EuiDataGridColumnCellActionProps) => { + const isComparison = isComparisonColumn(columnId); + const currentRow = isComparison ? comparisonRows[rowIndex] : baseRows[rowIndex]; + if (currentRow === undefined) { + return null; + } + const currentFrameId = getFrameIdentification(currentRow.frame); + + const isOpen = selectedFrame + ? selectedFrame.currentFrameId === currentFrameId && + selectedFrame.isComparison === isComparison + : false; + + const compareRow = isComparison + ? baseRows.find((item) => getFrameIdentification(item.frame) === currentFrameId) + : comparisonRows.find((item) => getFrameIdentification(item.frame) === currentFrameId); + + return ( + { + onClick({ currentFrameId, isComparison }); + }} + iconType="inspect" + > + {i18n.translate('xpack.profiling.compareFrame.component.findLabel', { + defaultMessage: 'Find corresponding frame', + })} + + } + isOpen={isOpen} + closePopover={() => { + onClick(undefined); + }} + anchorPosition="upRight" + css={css` + .euiPopover__anchor { + align-items: start; + display: flex; + } + `} + > + {compareRow ? ( +
    + + {isComparison + ? i18n.translate('xpack.profiling.diffTopNFunctions.baseLineFunction', { + defaultMessage: 'Baseline function', + }) + : i18n.translate('xpack.profiling.diffTopNFunctions.comparisonLineFunction', { + defaultMessage: 'Comparison function', + })} + + + {getCalleeFunction(compareRow.frame)} + + value.samples.toLocaleString(), + }, + { + field: 'selfCPUPerc', + name: 'Self CPU', + render: (_, value) => `${value.selfCPUPerc.toFixed(2)}%`, + }, + { + field: 'totalCPUPerc', + name: 'Total CPU', + render: (_, value) => `${value.totalCPUPerc.toFixed(2)}%`, + }, + ]} + /> +
    + ) : ( + + {i18n.translate('xpack.profiling.diffTopNFunctions.noCorrespondingValueFound', { + defaultMessage: 'No corresponding value found', + })} + + )} +
    + ); + }; diff --git a/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/index.tsx b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/index.tsx new file mode 100644 index 0000000000000..a006a4724a9bf --- /dev/null +++ b/x-pack/plugins/profiling/public/components/differential_topn_functions_grid/index.tsx @@ -0,0 +1,265 @@ +/* + * Copyright 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 { + EuiDataGrid, + EuiDataGridCellValueElementProps, + EuiDataGridColumn, + EuiDataGridSorting, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import { + getCalleeFunction, + StackFrameMetadata, + TopNComparisonFunctionSortField, + TopNFunctions, + TopNFunctionSortField, +} from '@kbn/profiling-utils'; +import { orderBy } from 'lodash'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useCalculateImpactEstimate } from '../../hooks/use_calculate_impact_estimates'; +import { FunctionRow } from '../topn_functions/function_row'; +import { getFunctionsRows, IFunctionRow } from '../topn_functions/utils'; +import { getColumns } from './get_columns'; +import { getCompareFrameAction } from './get_compare_frame_action'; + +const removeComparisonFromId = (id: string) => id.replace('comparison_', ''); +export const isComparisonColumn = (id: string) => id.startsWith('comparison_'); + +type SortDirection = 'asc' | 'desc'; + +function sortRows(data: IFunctionRow[], sortField: string, sortDirection: SortDirection) { + switch (sortField) { + case TopNFunctionSortField.Frame: + return orderBy(data, (row) => getCalleeFunction(row.frame), sortDirection); + case TopNFunctionSortField.SelfCPU: + return orderBy(data, (row) => row.selfCPUPerc, sortDirection); + case TopNFunctionSortField.TotalCPU: + return orderBy(data, (row) => row.totalCPUPerc, sortDirection); + default: + return orderBy(data, sortField, sortDirection); + } +} + +export type OnChangeSortParams = + | { sortField: TopNFunctionSortField; sortDirection: SortDirection } + | { + comparisonSortField: TopNComparisonFunctionSortField; + comparisonSortDirection: SortDirection; + }; + +export function getFrameIdentification(frame: StackFrameMetadata) { + return [ + frame.SourceFilename, + frame.FunctionName, + frame.ExeFileName, + frame.FileID, + frame.AddressOrLine, + ].join('|'); +} + +export interface SelectedFrame { + currentFrameId?: string; + isComparison: boolean; +} + +interface Props { + base?: TopNFunctions; + baselineScaleFactor?: number; + comparison?: TopNFunctions; + comparisonScaleFactor?: number; + onChangePage: (nextPage: number) => void; + onChangeSort: (sorting: OnChangeSortParams) => void; + onFrameClick?: (functionName: string) => void; + pageIndex: number; + sortDirection: 'asc' | 'desc'; + sortField: TopNFunctionSortField; + comparisonSortDirection: 'asc' | 'desc'; + comparisonSortField: TopNComparisonFunctionSortField; + totalSeconds: number; +} + +export function DifferentialTopNFunctionsGrid({ + base, + baselineScaleFactor, + comparison, + comparisonScaleFactor, + onChangePage, + onChangeSort, + pageIndex, + sortDirection, + sortField, + totalSeconds, + onFrameClick, + comparisonSortDirection, + comparisonSortField, +}: Props) { + const calculateImpactEstimates = useCalculateImpactEstimate(); + const [selectedFrame, setSelectedFrame] = useState(); + const theme = useEuiTheme(); + + const totalCount = useMemo(() => { + if (!base || !base.TotalCount) { + return 0; + } + + return base.TotalCount; + }, [base]); + + function onSort(newSortingColumns: EuiDataGridSorting['columns']) { + // As newSortingColumns is an array and we only sort by a single field for both base and comparison + // I need to look for the item that is not the same as in the URL to identify what's the side being sorted. + const sortingItem = newSortingColumns.reverse().find((item) => { + const isComparison = isComparisonColumn(item.id); + if (isComparison) { + return !(comparisonSortField === item.id && comparisonSortDirection === item.direction); + } + return !(sortField === item.id && sortDirection === item.direction); + }); + if (sortingItem) { + const isComparison = isComparisonColumn(sortingItem.id); + onChangeSort( + isComparison + ? { + comparisonSortDirection: sortingItem.direction, + comparisonSortField: sortingItem.id as TopNComparisonFunctionSortField, + } + : { + sortDirection: sortingItem.direction, + sortField: sortingItem.id as TopNFunctionSortField, + } + ); + } + } + + const { baseRows, comparisonRows } = useMemo(() => { + return { + baseRows: getFunctionsRows({ + calculateImpactEstimates, + topNFunctions: base, + totalSeconds: 900, + }), + comparisonRows: getFunctionsRows({ + baselineScaleFactor, + calculateImpactEstimates, + comparisonScaleFactor, + comparisonTopNFunctions: base, + topNFunctions: comparison, + totalSeconds, + }), + }; + }, [ + base, + baselineScaleFactor, + calculateImpactEstimates, + comparison, + comparisonScaleFactor, + totalSeconds, + ]); + + const columns: EuiDataGridColumn[] = useMemo(() => { + const compareFrameAction = getCompareFrameAction({ + baseRows, + comparisonRows, + onClick: setSelectedFrame, + selectedFrame, + }); + return getColumns(compareFrameAction); + }, [baseRows, comparisonRows, selectedFrame]); + + const sortedBaseRows = useMemo(() => { + return sortRows(baseRows, sortField, sortDirection); + }, [baseRows, sortDirection, sortField]); + + const sortedComparisonRows = useMemo(() => { + return sortRows( + comparisonRows, + removeComparisonFromId(comparisonSortField), + comparisonSortDirection + ); + }, [comparisonRows, comparisonSortDirection, comparisonSortField]); + + const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id)); + + function CellValue({ rowIndex, columnId, setCellProps }: EuiDataGridCellValueElementProps) { + const isComparison = isComparisonColumn(columnId); + const data = isComparison ? sortedComparisonRows[rowIndex] : sortedBaseRows[rowIndex]; + + useEffect(() => { + // Add thick border to divide the baseline and comparison columns + if (isComparison && columnId === TopNComparisonFunctionSortField.ComparisonRank) { + setCellProps({ + style: { borderLeft: theme.euiTheme.border.thick }, + }); + } else if (columnId === TopNFunctionSortField.TotalCPU) { + setCellProps({ + style: { borderRight: theme.euiTheme.border.thin }, + }); + } + }, [columnId, isComparison, setCellProps]); + + if (data === undefined) { + return null; + } + + return ( +
    + +
    + ); + } + + const rowCount = Math.min(Math.max(sortedBaseRows.length, sortedComparisonRows.length), 100); + + return ( + {}, + onChangePage, + pageSizeOptions: [], + }} + rowHeightsOptions={{ defaultHeight: 'auto' }} + toolbarVisibility={{ + showColumnSelector: false, + showKeyboardShortcuts: false, + showDisplaySelector: false, + showSortSelector: false, + }} + /> + ); +} diff --git a/x-pack/plugins/profiling/public/components/profiling_app_page_template/profiling_search_bar.tsx b/x-pack/plugins/profiling/public/components/profiling_app_page_template/profiling_search_bar.tsx index f1c52e6db5d0c..c41468f5dbff8 100644 --- a/x-pack/plugins/profiling/public/components/profiling_app_page_template/profiling_search_bar.tsx +++ b/x-pack/plugins/profiling/public/components/profiling_app_page_template/profiling_search_bar.tsx @@ -12,19 +12,10 @@ import React, { useEffect, useState } from 'react'; import { INDEX_EVENTS } from '../../../common'; import { useProfilingDependencies } from '../contexts/profiling_dependencies/use_profiling_dependencies'; -export function ProfilingSearchBar({ - kuery, - rangeFrom, - rangeTo, - onQuerySubmit, - onRefresh, - onRefreshClick, - showSubmitButton = true, - dataTestSubj = 'profilingUnifiedSearchBar', -}: { +interface Props { kuery: string; - rangeFrom: string; - rangeTo: string; + rangeFrom?: string; + rangeTo?: string; onQuerySubmit: ( payload: { dateRange: TimeRange; @@ -32,11 +23,26 @@ export function ProfilingSearchBar({ }, isUpdate?: boolean ) => void; - onRefresh: Required>['onRefresh']; + onRefresh?: Required>['onRefresh']; onRefreshClick: () => void; showSubmitButton?: boolean; dataTestSubj?: string; -}) { + showDatePicker?: boolean; + showQueryMenu?: boolean; +} + +export function ProfilingSearchBar({ + kuery, + rangeFrom, + rangeTo, + onQuerySubmit, + onRefresh, + onRefreshClick, + showSubmitButton = true, + dataTestSubj = 'profilingUnifiedSearchBar', + showDatePicker = true, + showQueryMenu = true, +}: Props) { const { start: { dataViews }, } = useProfilingDependencies(); @@ -67,7 +73,7 @@ export function ProfilingSearchBar({ onQuerySubmit({ dateRange, query }); }} showQueryInput - showDatePicker + showDatePicker={showDatePicker} showFilterBar={false} showSaveQuery={false} submitButtonStyle={!showSubmitButton ? 'iconOnly' : 'auto'} @@ -78,6 +84,7 @@ export function ProfilingSearchBar({ onRefresh={onRefresh} displayStyle="inPage" dataTestSubj={dataTestSubj} + showQueryMenu={showQueryMenu} /> ); } diff --git a/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx b/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx index 6cf0159dcdc01..d2421e704ac0b 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx +++ b/x-pack/plugins/profiling/public/components/topn_functions/function_row.tsx @@ -112,7 +112,7 @@ function DiffColumn({ diff, setCellProps }: DiffColumnProps) { const dangerColor = useEuiBackgroundColor('danger'); useEffect(() => { - if (diff && diff.rank > 0) { + if (diff && diff.rank !== 0) { const color = diff.rank > 0 ? 'success' : 'danger'; setCellProps({ style: { backgroundColor: color === 'success' ? successColor : dangerColor }, diff --git a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts index 788c7397fa79d..e09b404b5c60c 100644 --- a/x-pack/plugins/profiling/public/components/topn_functions/utils.ts +++ b/x-pack/plugins/profiling/public/components/topn_functions/utils.ts @@ -33,6 +33,7 @@ export function scaleValue({ value, scaleFactor = 1 }: { value: number; scaleFac } export interface IFunctionRow { + id: string; rank: number; frame: StackFrameMetadata; samples: number; @@ -106,6 +107,7 @@ export function getFunctionsRows({ const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU; return { + id: comparisonRow.Id, rank: topN.Rank - comparisonRow.Rank, samples: scaledDiffSamples, selfCPU: comparisonRow.CountExclusive, @@ -120,6 +122,7 @@ export function getFunctionsRows({ } return { + id: topN.Id, rank: topN.Rank, frame: topN.Frame, samples: scaledSelfCPU, diff --git a/x-pack/plugins/profiling/public/embeddables/functions/embeddable_functions.tsx b/x-pack/plugins/profiling/public/embeddables/functions/embeddable_functions.tsx index 4cfbe7ceddbb8..9a198ae66e262 100644 --- a/x-pack/plugins/profiling/public/embeddables/functions/embeddable_functions.tsx +++ b/x-pack/plugins/profiling/public/embeddables/functions/embeddable_functions.tsx @@ -9,12 +9,12 @@ import { EMBEDDABLE_FUNCTIONS } from '@kbn/observability-shared-plugin/public'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { AsyncEmbeddableComponent } from '../async_embeddable_component'; -import { EmbeddableFunctionsEmbeddableInput } from './embeddable_functions_factory'; -import { EmbeddableFunctionsGrid } from './embeddable_functions_grid'; import { ProfilingEmbeddableProvider, ProfilingEmbeddablesDependencies, } from '../profiling_embeddable_provider'; +import { EmbeddableFunctionsEmbeddableInput } from './embeddable_functions_factory'; +import { EmbeddableFunctionsGrid } from './embeddable_functions_grid'; export class EmbeddableFunctions extends Embeddable< EmbeddableFunctionsEmbeddableInput, diff --git a/x-pack/plugins/profiling/public/embeddables/profiling_embeddable_provider.tsx b/x-pack/plugins/profiling/public/embeddables/profiling_embeddable_provider.tsx index d4db1e2d9fb77..b8defbdfa4acb 100644 --- a/x-pack/plugins/profiling/public/embeddables/profiling_embeddable_provider.tsx +++ b/x-pack/plugins/profiling/public/embeddables/profiling_embeddable_provider.tsx @@ -9,6 +9,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; import React, { ReactChild, useMemo } from 'react'; import { CoreSetup, CoreStart } from '@kbn/core/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; import { ProfilingDependenciesContextProvider } from '../components/contexts/profiling_dependencies/profiling_dependencies_context'; import { ProfilingPluginPublicSetupDeps, ProfilingPluginPublicStartDeps } from '../types'; import { Services } from '../services'; @@ -21,6 +22,8 @@ export interface ProfilingEmbeddablesDependencies { profilingFetchServices: Services; } +const storage = new Storage(localStorage); + export type GetProfilingEmbeddableDependencies = () => Promise; interface Props { @@ -44,13 +47,17 @@ export function ProfilingEmbeddableProvider({ deps, children }: Props) { [deps] ); + const i18nCore = deps.coreStart.i18n; + return ( - - - - {children} - - - + + + + + {children} + + + + ); } diff --git a/x-pack/plugins/profiling/public/embeddables/register_embeddables.ts b/x-pack/plugins/profiling/public/embeddables/register_embeddables.ts index d7b2e947144bd..2c229414960a6 100644 --- a/x-pack/plugins/profiling/public/embeddables/register_embeddables.ts +++ b/x-pack/plugins/profiling/public/embeddables/register_embeddables.ts @@ -9,10 +9,12 @@ import { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import { EMBEDDABLE_FLAMEGRAPH, EMBEDDABLE_FUNCTIONS, + EMBEDDABLE_PROFILING_SEARCH_BAR, } from '@kbn/observability-shared-plugin/public'; import { EmbeddableFlamegraphFactory } from './flamegraph/embeddable_flamegraph_factory'; import { EmbeddableFunctionsFactory } from './functions/embeddable_functions_factory'; import { GetProfilingEmbeddableDependencies } from './profiling_embeddable_provider'; +import { EmbeddableSearchBarFactory } from './search_bar/embeddable_search_bar_factory'; export function registerEmbeddables( embeddable: EmbeddableSetup, @@ -26,4 +28,8 @@ export function registerEmbeddables( EMBEDDABLE_FUNCTIONS, new EmbeddableFunctionsFactory(getProfilingEmbeddableDependencies) ); + embeddable.registerEmbeddableFactory( + EMBEDDABLE_PROFILING_SEARCH_BAR, + new EmbeddableSearchBarFactory(getProfilingEmbeddableDependencies) + ); } diff --git a/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar.tsx b/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar.tsx new file mode 100644 index 0000000000000..cbc773abfc0f7 --- /dev/null +++ b/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { css } from '@emotion/react'; +import { Embeddable, EmbeddableOutput, IContainer } from '@kbn/embeddable-plugin/public'; +import { EMBEDDABLE_PROFILING_SEARCH_BAR } from '@kbn/observability-shared-plugin/public'; +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { ProfilingSearchBar } from '../../components/profiling_app_page_template/profiling_search_bar'; +import { + ProfilingEmbeddableProvider, + ProfilingEmbeddablesDependencies, +} from '../profiling_embeddable_provider'; +import { EmbeddableSearchBarEmbeddableInput } from './embeddable_search_bar_factory'; + +export class EmbeddableSearchBar extends Embeddable< + EmbeddableSearchBarEmbeddableInput, + EmbeddableOutput +> { + readonly type = EMBEDDABLE_PROFILING_SEARCH_BAR; + private _domNode?: HTMLElement; + + constructor( + private deps: ProfilingEmbeddablesDependencies, + initialInput: EmbeddableSearchBarEmbeddableInput, + parent?: IContainer + ) { + super(initialInput, {}, parent); + } + + render(domNode: HTMLElement) { + this._domNode = domNode; + const { showDatePicker, kuery, onQuerySubmit, onRefresh, rangeFrom, rangeTo } = this.input; + + render( + +
    + { + onQuerySubmit({ + dateRange, + query: typeof query?.query === 'string' ? query.query : '', + }); + }} + onRefresh={onRefresh} + onRefreshClick={onRefresh} + showQueryMenu={false} + rangeFrom={rangeFrom} + rangeTo={rangeTo} + /> +
    +
    , + domNode + ); + } + + public destroy() { + if (this._domNode) { + unmountComponentAtNode(this._domNode); + } + } + + reload() { + if (this._domNode) { + this.render(this._domNode); + } + } +} diff --git a/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar_factory.ts b/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar_factory.ts new file mode 100644 index 0000000000000..cc7443976e1b1 --- /dev/null +++ b/x-pack/plugins/profiling/public/embeddables/search_bar/embeddable_search_bar_factory.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + EmbeddableFactoryDefinition, + EmbeddableInput, + IContainer, +} from '@kbn/embeddable-plugin/public'; +import { + EmbeddableProfilingSearchBarProps, + EMBEDDABLE_PROFILING_SEARCH_BAR, +} from '@kbn/observability-shared-plugin/public'; +import type { GetProfilingEmbeddableDependencies } from '../profiling_embeddable_provider'; + +export type EmbeddableSearchBarEmbeddableInput = EmbeddableProfilingSearchBarProps & + EmbeddableInput; + +export class EmbeddableSearchBarFactory + implements EmbeddableFactoryDefinition +{ + readonly type = EMBEDDABLE_PROFILING_SEARCH_BAR; + + constructor(private getProfilingEmbeddableDependencies: GetProfilingEmbeddableDependencies) {} + + async isEditable() { + return false; + } + + async create(input: EmbeddableSearchBarEmbeddableInput, parent?: IContainer) { + const { EmbeddableSearchBar } = await import('./embeddable_search_bar'); + const deps = await this.getProfilingEmbeddableDependencies(); + return new EmbeddableSearchBar(deps, input, parent); + } + + getDisplayName() { + return 'Universal Profiling Search bar'; + } +} diff --git a/x-pack/plugins/profiling/public/routing/index.tsx b/x-pack/plugins/profiling/public/routing/index.tsx index cb92422a94076..9df36e393c389 100644 --- a/x-pack/plugins/profiling/public/routing/index.tsx +++ b/x-pack/plugins/profiling/public/routing/index.tsx @@ -6,33 +6,35 @@ */ import { i18n } from '@kbn/i18n'; import { toNumberRt } from '@kbn/io-ts-utils'; -import { createRouter, Outlet } from '@kbn/typed-react-router-config'; -import * as t from 'io-ts'; -import React from 'react'; import { StackTracesDisplayOption, - TopNType, + TopNComparisonFunctionSortField, + topNComparisonFunctionSortFieldRt, TopNFunctionSortField, topNFunctionSortFieldRt, + TopNType, } from '@kbn/profiling-utils'; +import { createRouter, Outlet } from '@kbn/typed-react-router-config'; +import * as t from 'io-ts'; +import React from 'react'; import { indexLifecyclePhaseRt, IndexLifecyclePhaseSelectOption, } from '../../common/storage_explorer'; import { ComparisonMode, NormalizationMode } from '../components/normalization_menu'; import { RedirectTo } from '../components/redirect_to'; +import { AddDataTabs, AddDataView } from '../views/add_data_view'; +import { DeleteDataView } from '../views/delete_data_view'; import { FlameGraphsView } from '../views/flamegraphs'; import { DifferentialFlameGraphsView } from '../views/flamegraphs/differential_flamegraphs'; import { FlameGraphView } from '../views/flamegraphs/flamegraph'; import { FunctionsView } from '../views/functions'; import { DifferentialTopNFunctionsView } from '../views/functions/differential_topn'; import { TopNFunctionsView } from '../views/functions/topn'; -import { AddDataTabs, AddDataView } from '../views/add_data_view'; +import { Settings } from '../views/settings'; import { StackTracesView } from '../views/stack_traces_view'; import { StorageExplorerView } from '../views/storage_explorer'; import { RouteBreadcrumb } from './route_breadcrumb'; -import { DeleteDataView } from '../views/delete_data_view'; -import { Settings } from '../views/settings'; const routes = { '/settings': { @@ -253,6 +255,8 @@ const routes = { t.literal(NormalizationMode.Scale), t.literal(NormalizationMode.Time), ]), + comparisonSortField: topNComparisonFunctionSortFieldRt, + comparisonSortDirection: t.union([t.literal('asc'), t.literal('desc')]), }), t.partial({ baseline: toNumberRt, @@ -267,6 +271,8 @@ const routes = { comparisonRangeTo: 'now', comparisonKuery: '', normalizationMode: NormalizationMode.Time, + comparisonSortField: TopNComparisonFunctionSortField.ComparisonRank, + comparisonSortDirection: 'asc', }, }, }, diff --git a/x-pack/plugins/profiling/public/types.ts b/x-pack/plugins/profiling/public/types.ts index e583a4962dc68..cc949254e2707 100644 --- a/x-pack/plugins/profiling/public/types.ts +++ b/x-pack/plugins/profiling/public/types.ts @@ -25,6 +25,10 @@ import { ObservabilityAIAssistantPluginStart, } from '@kbn/observability-ai-assistant-plugin/public'; import { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; +import type { + UnifiedSearchPublicPluginStart, + UnifiedSearchPluginSetup, +} from '@kbn/unified-search-plugin/public'; export interface ProfilingPluginPublicSetupDeps { observability: ObservabilityPublicSetup; @@ -36,6 +40,7 @@ export interface ProfilingPluginPublicSetupDeps { licensing: LicensingPluginSetup; share: SharePluginSetup; embeddable: EmbeddableSetup; + unifiedSearch: UnifiedSearchPluginSetup; } export interface ProfilingPluginPublicStartDeps { @@ -46,4 +51,5 @@ export interface ProfilingPluginPublicStartDeps { data: DataPublicPluginStart; charts: ChartsPluginStart; share: SharePluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; } diff --git a/x-pack/plugins/profiling/public/views/add_data_view/index.tsx b/x-pack/plugins/profiling/public/views/add_data_view/index.tsx index 556e29c1ba240..27e3ac639757a 100644 --- a/x-pack/plugins/profiling/public/views/add_data_view/index.tsx +++ b/x-pack/plugins/profiling/public/views/add_data_view/index.tsx @@ -541,7 +541,7 @@ docker.elastic.co/observability/profiling-agent:${stackVersion} /root/pf-host-ag title={ diff --git a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx index cf538642afce8..0c5c45f60fcb7 100644 --- a/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx +++ b/x-pack/plugins/profiling/public/views/functions/differential_topn/index.tsx @@ -4,28 +4,21 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { - EuiDataGridRefProps, - EuiDataGridSorting, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiPanel, - EuiSpacer, -} from '@elastic/eui'; -import { TopNFunctionSortField } from '@kbn/profiling-utils'; -import React, { useRef } from 'react'; -import { GridOnScrollProps } from 'react-window'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiPanel, EuiSpacer } from '@elastic/eui'; +import React from 'react'; import { AsyncComponent } from '../../../components/async_component'; import { useProfilingDependencies } from '../../../components/contexts/profiling_dependencies/use_profiling_dependencies'; import { FramesSummary } from '../../../components/frames_summary'; +import { + DifferentialTopNFunctionsGrid, + OnChangeSortParams, +} from '../../../components/differential_topn_functions_grid'; import { NormalizationMenu, NormalizationMode, NormalizationOptions, } from '../../../components/normalization_menu'; import { PrimaryAndComparisonSearchBar } from '../../../components/primary_and_comparison_search_bar'; -import { TopNFunctionsGrid } from '../../../components/topn_functions'; import { AsyncStatus } from '../../../hooks/use_async'; import { useProfilingParams } from '../../../hooks/use_profiling_params'; import { useProfilingRouter } from '../../../hooks/use_profiling_router'; @@ -34,8 +27,6 @@ import { useTimeRange } from '../../../hooks/use_time_range'; import { useTimeRangeAsync } from '../../../hooks/use_time_range_async'; export function DifferentialTopNFunctionsView() { - const baseGridRef = useRef(null); - const comparisonGridRef = useRef(null); const { query } = useProfilingParams('/functions/differential'); const { rangeFrom, @@ -50,6 +41,8 @@ export function DifferentialTopNFunctionsView() { baseline = 1, comparison = 1, pageIndex = 0, + comparisonSortDirection, + comparisonSortField, } = query; const timeRange = useTimeRange({ rangeFrom, rangeTo }); @@ -147,20 +140,6 @@ export function DifferentialTopNFunctionsView() { }); } - function handleBaseGridScroll(scroll: GridOnScrollProps) { - if (comparisonGridRef?.current?.scrollTo) { - comparisonGridRef.current.scrollTo({ - scrollTop: scroll.scrollTop, - }); - } - } - - function handleComparisonGridScroll(scroll: GridOnScrollProps) { - if (baseGridRef?.current?.scrollTo) { - baseGridRef.current.scrollTo({ scrollTop: scroll.scrollTop }); - } - } - function handlePageChange(nextPage: number) { profilingRouter.push('/functions/differential', { path: {}, @@ -168,14 +147,10 @@ export function DifferentialTopNFunctionsView() { }); } - function handleSortChange(sorting: EuiDataGridSorting['columns'][0]) { + function handleOnSort(sorting: OnChangeSortParams) { profilingRouter.push('/functions/differential', { path: {}, - query: { - ...query, - sortField: sorting.id as TopNFunctionSortField, - sortDirection: sorting.direction, - }, + query: { ...query, ...sorting }, }); } @@ -223,50 +198,27 @@ export function DifferentialTopNFunctionsView() { - - - - - - - {comparisonTimeRange.inSeconds.start && comparisonTimeRange.inSeconds.end ? ( - - - - - - ) : null} - + + + diff --git a/x-pack/plugins/profiling/public/views/functions/index.tsx b/x-pack/plugins/profiling/public/views/functions/index.tsx index 1131056d9a9b8..2e4ddb32bc030 100644 --- a/x-pack/plugins/profiling/public/views/functions/index.tsx +++ b/x-pack/plugins/profiling/public/views/functions/index.tsx @@ -8,6 +8,7 @@ import { EuiPageHeaderContentProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { TopNComparisonFunctionSortField } from '@kbn/profiling-utils'; import { NormalizationMode } from '../../components/normalization_menu'; import { ProfilingAppPageTemplate } from '../../components/profiling_app_page_template'; import { RedirectTo } from '../../components/redirect_to'; @@ -51,6 +52,12 @@ export function FunctionsView({ children }: { children: React.ReactElement }) { comparisonKuery: query.kuery, normalizationMode: 'normalizationMode' in query ? query.normalizationMode : NormalizationMode.Time, + comparisonSortField: + 'comparisonSortField' in query + ? query.comparisonSortField + : TopNComparisonFunctionSortField.ComparisonRank, + comparisonSortDirection: + 'comparisonSortDirection' in query ? query.comparisonSortDirection : 'asc', }, }), }, diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_setup_trust/confirm_modal.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_setup_trust/confirm_modal.tsx index 08ef4377338d1..1d9e9f1900f09 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_setup_trust/confirm_modal.tsx +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_setup_trust/confirm_modal.tsx @@ -70,6 +70,9 @@ export const ConfirmTrustSetupModal = ({ closeModal, onSubmit }: ModalProps) => label={i18n.translate('xpack.remoteClusters.clusterWizard.trustStep.modal.checkbox', { defaultMessage: 'Yes, I have setup trust', })} + labelProps={{ + 'data-test-subj': 'remoteClusterTrustCheckboxLabel', + }} checked={hasSetupTrust} onChange={() => setHasSetupTrust(!hasSetupTrust)} data-test-subj="remoteClusterTrustCheckbox" diff --git a/x-pack/plugins/reporting/public/lib/default_status_context.tsx b/x-pack/plugins/reporting/public/lib/default_status_context.tsx new file mode 100644 index 0000000000000..23609e7e0a6ab --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/default_status_context.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClientConfigType } from '@kbn/reporting-public'; +import React, { createContext, FunctionComponent } from 'react'; +import { IlmPolicyStatusContextProvider } from './ilm_policy_status_context'; + +const PolicyStatusContext = createContext(undefined); + +interface PolicyStatusContextProviderProps { + config: ClientConfigType; +} + +export const PolicyStatusContextProvider: FunctionComponent = ({ + children, + ...props +}) => { + return props.config.statefulSettings.enabled ? ( + {children} + ) : ( + {children} + ); +}; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts index 5ede6e11ee78a..6668e9efe881f 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client/hooks.ts @@ -13,5 +13,6 @@ export const useCheckIlmPolicyStatus = (): UseRequestResponse diff --git a/x-pack/plugins/reporting/public/management/default/report_listing_default.tsx b/x-pack/plugins/reporting/public/management/default/report_listing_default.tsx new file mode 100644 index 0000000000000..a2b09ccd3c177 --- /dev/null +++ b/x-pack/plugins/reporting/public/management/default/report_listing_default.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, { FC } from 'react'; + +import { EuiPageHeader, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { ListingPropsInternal } from '..'; +import { ReportListingTable } from '../report_listing_table'; + +/** + * Used in non-stateful (Serverless) + * Does not render controls for features only applicable in Stateful + */ +export const ReportListingDefault: FC = (props) => { + const { apiClient, capabilities, config, navigateToUrl, toasts, urlService, ...listingProps } = + props; + return ( + <> + + } + description={ + + } + /> + + + + ); +}; diff --git a/x-pack/plugins/reporting/public/management/index.ts b/x-pack/plugins/reporting/public/management/index.ts index 4b9074267dd49..bdd6d0a9916b7 100644 --- a/x-pack/plugins/reporting/public/management/index.ts +++ b/x-pack/plugins/reporting/public/management/index.ts @@ -8,20 +8,21 @@ import type { ApplicationStart, ToastsSetup } from '@kbn/core/public'; import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { ClientConfigType } from '@kbn/reporting-public'; -import type { UseIlmPolicyStatusReturn } from '../lib/ilm_policy_status_context'; import type { ReportingAPIClient } from '../lib/reporting_api_client'; import type { SharePluginSetup } from '../shared_imports'; export interface ListingProps { apiClient: ReportingAPIClient; - capabilities: ApplicationStart['capabilities']; license$: LicensingPluginStart['license$']; config: ClientConfigType; redirect: ApplicationStart['navigateToApp']; navigateToUrl: ApplicationStart['navigateToUrl']; toasts: ToastsSetup; urlService: SharePluginSetup['url']; - ilmPolicyContextValue: UseIlmPolicyStatusReturn; } +export type ListingPropsInternal = ListingProps & { + capabilities: ApplicationStart['capabilities']; +}; + export { ReportListing } from './report_listing'; diff --git a/x-pack/plugins/reporting/public/management/mount_management_section.tsx b/x-pack/plugins/reporting/public/management/mount_management_section.tsx index 60cd66d952780..575d9e7c8d7cc 100644 --- a/x-pack/plugins/reporting/public/management/mount_management_section.tsx +++ b/x-pack/plugins/reporting/public/management/mount_management_section.tsx @@ -15,10 +15,10 @@ import { ILicense } from '@kbn/licensing-plugin/public'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import type { ClientConfigType } from '@kbn/reporting-public'; import { ReportListing } from '.'; -import { IlmPolicyStatusContextProvider } from '../lib/ilm_policy_status_context'; import { InternalApiClientProvider, ReportingAPIClient } from '../lib/reporting_api_client'; import type { ManagementAppMountParams, SharePluginSetup } from '../shared_imports'; import { KibanaContextProvider } from '../shared_imports'; +import { PolicyStatusContextProvider } from '../lib/default_status_context'; export async function mountManagementSection( coreSetup: CoreSetup, @@ -41,8 +41,9 @@ export async function mountManagementSection( }} > - + - + diff --git a/x-pack/plugins/reporting/public/management/report_listing.tsx b/x-pack/plugins/reporting/public/management/report_listing.tsx index 31976dd196a9a..024d75b04ae10 100644 --- a/x-pack/plugins/reporting/public/management/report_listing.tsx +++ b/x-pack/plugins/reporting/public/management/report_listing.tsx @@ -5,529 +5,26 @@ * 2.0. */ -import { Component, Fragment, default as React } from 'react'; -import { Subscription } from 'rxjs'; - -import { - EuiBasicTable, - EuiBasicTableColumn, - EuiFlexGroup, - EuiFlexItem, - EuiIconTip, - EuiLink, - EuiLoadingSpinner, - EuiPageHeader, - EuiSpacer, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { ILicense } from '@kbn/licensing-plugin/public'; -import { durationToNumber } from '@kbn/reporting-common'; +import React from 'react'; import { ListingProps as Props } from '.'; -import { REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '../../common/constants'; -import { prettyPrintJobType } from '../../common/job_utils'; -import { Poller } from '../../common/poller'; -import { useIlmPolicyStatus } from '../lib/ilm_policy_status_context'; -import { Job } from '../lib/job'; -import { checkLicense } from '../lib/license_check'; import { useInternalApiClient } from '../lib/reporting_api_client'; import { useKibana } from '../shared_imports'; -import { - IlmPolicyLink, - MigrateIlmPolicyCallOut, - ReportDeleteButton, - ReportDiagnostic, - ReportInfoFlyout, - ReportStatusIndicator, -} from './components'; -import { guessAppIconTypeFromObjectType } from './utils'; import './report_listing.scss'; +import { ReportListingStateful } from './stateful/report_listing_stateful'; +import { ReportListingDefault } from './default/report_listing_default'; -type TableColumn = EuiBasicTableColumn; - -interface State { - page: number; - total: number; - jobs: Job[]; - selectedJobs: Job[]; - isLoading: boolean; - showLinks: boolean; - enableLinks: boolean; - badLicenseMessage: string; - selectedJob: undefined | Job; -} - -class ReportListingUi extends Component { - private isInitialJobsFetch: boolean; - private licenseSubscription?: Subscription; - private mounted?: boolean; - private poller?: Poller; - - constructor(props: Props) { - super(props); - - this.state = { - page: 0, - total: 0, - jobs: [], - selectedJobs: [], - isLoading: false, - showLinks: false, - enableLinks: false, - badLicenseMessage: '', - selectedJob: undefined, - }; - - this.isInitialJobsFetch = true; - } - - public render() { - const { - apiClient, - toasts, - ilmPolicyContextValue, - urlService, - navigateToUrl, - capabilities, - config, - } = this.props; - const ilmLocator = urlService.locators.get('ILM_LOCATOR_ID'); - const hasIlmPolicy = ilmPolicyContextValue.status !== 'policy-not-found'; - const showIlmPolicyLink = Boolean(ilmLocator && hasIlmPolicy); - return ( - <> - - } - description={ - - } - /> - - {config.statefulSettings.enabled ? : null} - - -
    {this.renderTable()}
    - - - - {capabilities?.management?.data?.index_lifecycle_management && ( - - {ilmPolicyContextValue.isLoading ? ( - - ) : ( - showIlmPolicyLink && ( - - ) - )} - - )} - - - - - - ); - } - - public componentWillUnmount() { - this.mounted = false; - this.poller?.stop(); - - if (this.licenseSubscription) { - this.licenseSubscription.unsubscribe(); - } - } - - public componentDidMount() { - this.mounted = true; - const { config, license$ } = this.props; - const pollFrequencyInMillis = durationToNumber(config.poll.jobsRefresh.interval); - this.poller = new Poller({ - functionToPoll: () => { - return this.fetchJobs(); - }, - pollFrequencyInMillis, - trailing: false, - continuePollingOnError: true, - pollFrequencyErrorMultiplier: config.poll.jobsRefresh.intervalErrorMultiplier, - }); - this.poller.start(); - this.licenseSubscription = license$.subscribe(this.licenseHandler); - } - - private licenseHandler = (license: ILicense) => { - const { - enableLinks, - showLinks, - message: badLicenseMessage, - } = checkLicense(license.check('reporting', 'basic')); - - this.setState({ - enableLinks, - showLinks, - badLicenseMessage, - }); - }; - - private onSelectionChange = (jobs: Job[]) => { - this.setState((current) => ({ ...current, selectedJobs: jobs })); - }; - - private removeJob = (job: Job) => { - const { jobs } = this.state; - const filtered = jobs.filter((j) => j.id !== job.id); - this.setState((current) => ({ ...current, jobs: filtered })); - }; - - private renderDeleteButton = () => { - const { selectedJobs } = this.state; - if (selectedJobs.length === 0) return undefined; - - const performDelete = async () => { - for (const job of selectedJobs) { - try { - await this.props.apiClient.deleteReport(job.id); - this.removeJob(job); - this.props.toasts.addSuccess( - i18n.translate('xpack.reporting.listing.table.deleteConfim', { - defaultMessage: `The {reportTitle} report was deleted`, - values: { - reportTitle: job.title, - }, - }) - ); - } catch (error) { - this.props.toasts.addDanger( - i18n.translate('xpack.reporting.listing.table.deleteFailedErrorMessage', { - defaultMessage: `The report was not deleted: {error}`, - values: { error }, - }) - ); - throw error; - } - } - }; - - return ( - - ); - }; - - private onTableChange = ({ page }: { page: { index: number } }) => { - const { index: pageIndex } = page; - this.setState(() => ({ page: pageIndex }), this.fetchJobs); - }; - - private fetchJobs = async () => { - // avoid page flicker when poller is updating table - only display loading screen on first load - if (this.isInitialJobsFetch) { - this.setState(() => ({ isLoading: true })); - } - - let jobs: Job[]; - let total: number; - try { - jobs = await this.props.apiClient.list(this.state.page); - total = await this.props.apiClient.total(); - this.isInitialJobsFetch = false; - } catch (fetchError) { - if (!this.licenseAllowsToShowThisPage()) { - this.props.toasts.addDanger(this.state.badLicenseMessage); - this.props.redirect('management'); - return; - } - - if (fetchError.message === 'Failed to fetch') { - this.props.toasts.addDanger( - fetchError.message || - i18n.translate('xpack.reporting.listing.table.requestFailedErrorMessage', { - defaultMessage: 'Request failed', - }) - ); - } - if (this.mounted) { - this.setState(() => ({ isLoading: false, jobs: [], total: 0 })); - } - return; - } - - if (this.mounted) { - this.setState(() => ({ - isLoading: false, - total, - jobs, - })); - } - }; - - private licenseAllowsToShowThisPage = () => { - return this.state.showLinks && this.state.enableLinks; - }; - - /** - * Widths like this are not the best, but the auto-layout does not play well with text in links. We can update - * this with something that works better on all screen sizes. This works for desktop, mobile fallback is provided on a - * per column basis. - */ - private readonly tableColumnWidths = { - type: '5%', - title: '30%', - status: '20%', - createdAt: '25%', - content: '10%', - actions: '10%', - }; - - private renderTable() { - const { tableColumnWidths } = this; - const tableColumns: TableColumn[] = [ - { - field: 'type', - width: tableColumnWidths.type, - name: i18n.translate('xpack.reporting.listing.tableColumns.typeTitle', { - defaultMessage: 'Type', - }), - render: (_type: string, job) => { - return ( -
    - -
    - ); - }, - mobileOptions: { - show: true, - render: (job) => { - return
    {job.objectType}
    ; - }, - }, - }, - { - field: 'title', - name: i18n.translate('xpack.reporting.listing.tableColumns.reportTitle', { - defaultMessage: 'Title', - }), - width: tableColumnWidths.title, - render: (objectTitle: string, job) => { - return ( -
    - this.setState({ selectedJob: job })} - > - {objectTitle || - i18n.translate('xpack.reporting.listing.table.noTitleLabel', { - defaultMessage: 'Untitled', - })} - -
    - ); - }, - mobileOptions: { - header: false, - width: '100%', // This is not recognized by EUI types but has an effect, leaving for now - } as unknown as { header: boolean }, - }, - { - field: 'status', - width: tableColumnWidths.status, - name: i18n.translate('xpack.reporting.listing.tableColumns.statusTitle', { - defaultMessage: 'Status', - }), - render: (_status: string, job) => { - return ( - - - - ); - }, - mobileOptions: { - show: false, - }, - }, - { - field: 'created_at', - width: tableColumnWidths.createdAt, - name: i18n.translate('xpack.reporting.listing.tableColumns.createdAtTitle', { - defaultMessage: 'Created at', - }), - render: (_createdAt: string, job) => ( -
    {job.getCreatedAtDate()}
    - ), - mobileOptions: { - show: false, - }, - }, - { - field: 'content', - width: tableColumnWidths.content, - name: i18n.translate('xpack.reporting.listing.tableColumns.content', { - defaultMessage: 'Content', - }), - render: (_status: string, job) => prettyPrintJobType(job.jobtype), - mobileOptions: { - show: false, - }, - }, - { - name: i18n.translate('xpack.reporting.listing.tableColumns.actionsTitle', { - defaultMessage: 'Actions', - }), - width: tableColumnWidths.actions, - actions: [ - { - isPrimary: true, - 'data-test-subj': 'reportDownloadLink', - type: 'icon', - icon: 'download', - name: i18n.translate('xpack.reporting.listing.table.downloadReportButtonLabel', { - defaultMessage: 'Download report', - }), - description: i18n.translate('xpack.reporting.listing.table.downloadReportDescription', { - defaultMessage: 'Download this report in a new tab.', - }), - onClick: (job) => this.props.apiClient.downloadReport(job.id), - enabled: (job) => job.isDownloadReady, - }, - { - name: i18n.translate( - 'xpack.reporting.listing.table.viewReportingInfoActionButtonLabel', - { - defaultMessage: 'View report info', - } - ), - description: i18n.translate( - 'xpack.reporting.listing.table.viewReportingInfoActionButtonDescription', - { - defaultMessage: 'View additional information about this report.', - } - ), - type: 'icon', - icon: 'iInCircle', - onClick: (job) => this.setState({ selectedJob: job }), - }, - { - name: i18n.translate('xpack.reporting.listing.table.openInKibanaAppLabel', { - defaultMessage: 'Open in Kibana', - }), - 'data-test-subj': 'reportOpenInKibanaApp', - description: i18n.translate( - 'xpack.reporting.listing.table.openInKibanaAppDescription', - { - defaultMessage: 'Open the Kibana app where this report was generated.', - } - ), - available: (job) => job.canLinkToKibanaApp, - type: 'icon', - icon: 'popout', - onClick: (job) => { - const href = this.props.apiClient.getKibanaAppHref(job); - window.open(href, '_blank'); - window.focus(); - }, - }, - ], - }, - ]; - - const pagination = { - pageIndex: this.state.page, - pageSize: 10, - totalItemCount: this.state.total, - showPerPageOptions: false, - }; - - const selection = { - itemId: 'id', - onSelectionChange: this.onSelectionChange, - }; - - return ( - - {this.state.selectedJobs.length > 0 && ( - - - {this.renderDeleteButton()} - - - - )} - ({ 'data-test-subj': REPORT_TABLE_ROW_ID })} - /> - {!!this.state.selectedJob && ( - this.setState({ selectedJob: undefined })} - job={this.state.selectedJob} - /> - )} - - ); - } -} - -export const ReportListing = ( - props: Omit< - Props, - 'ilmPolicyContextValue' | 'intl' | 'apiClient' | 'capabilities' | 'configAllowsImages' - > -) => { - const ilmPolicyStatusValue = useIlmPolicyStatus(); +export const ReportListing = (props: Props) => { const { apiClient } = useInternalApiClient(); const { services: { application: { capabilities }, }, } = useKibana(); - return ( - + return props.config.statefulSettings.enabled ? ( + + ) : ( + ); }; diff --git a/x-pack/plugins/reporting/public/management/report_listing_table.tsx b/x-pack/plugins/reporting/public/management/report_listing_table.tsx new file mode 100644 index 0000000000000..c8dcbf77aba30 --- /dev/null +++ b/x-pack/plugins/reporting/public/management/report_listing_table.tsx @@ -0,0 +1,440 @@ +/* + * Copyright 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 { Component, Fragment, default as React } from 'react'; +import { Subscription } from 'rxjs'; + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiLink, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ILicense } from '@kbn/licensing-plugin/public'; +import { durationToNumber } from '@kbn/reporting-common'; + +import { REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '../../common/constants'; +import { prettyPrintJobType } from '../../common/job_utils'; +import { Poller } from '../../common/poller'; +import { Job } from '../lib/job'; +import { checkLicense } from '../lib/license_check'; +import { ReportDeleteButton, ReportInfoFlyout, ReportStatusIndicator } from './components'; +import { guessAppIconTypeFromObjectType } from './utils'; +import { ListingPropsInternal } from '.'; + +type TableColumn = EuiBasicTableColumn; + +interface State { + page: number; + total: number; + jobs: Job[]; + selectedJobs: Job[]; + isLoading: boolean; + showLinks: boolean; + enableLinks: boolean; + badLicenseMessage: string; + selectedJob: undefined | Job; +} + +export class ReportListingTable extends Component { + private isInitialJobsFetch: boolean; + private licenseSubscription?: Subscription; + private mounted?: boolean; + private poller?: Poller; + + constructor(props: ListingPropsInternal) { + super(props); + + this.state = { + page: 0, + total: 0, + jobs: [], + selectedJobs: [], + isLoading: false, + showLinks: false, + enableLinks: false, + badLicenseMessage: '', + selectedJob: undefined, + }; + + this.isInitialJobsFetch = true; + } + + public componentWillUnmount() { + this.mounted = false; + this.poller?.stop(); + + if (this.licenseSubscription) { + this.licenseSubscription.unsubscribe(); + } + } + + public componentDidMount() { + this.mounted = true; + const { config, license$ } = this.props; + const pollFrequencyInMillis = durationToNumber(config.poll.jobsRefresh.interval); + this.poller = new Poller({ + functionToPoll: () => { + return this.fetchJobs(); + }, + pollFrequencyInMillis, + trailing: false, + continuePollingOnError: true, + pollFrequencyErrorMultiplier: config.poll.jobsRefresh.intervalErrorMultiplier, + }); + this.poller.start(); + this.licenseSubscription = license$.subscribe(this.licenseHandler); + } + + private licenseHandler = (license: ILicense) => { + const { + enableLinks, + showLinks, + message: badLicenseMessage, + } = checkLicense(license.check('reporting', 'basic')); + + this.setState({ + enableLinks, + showLinks, + badLicenseMessage, + }); + }; + + private onSelectionChange = (jobs: Job[]) => { + this.setState((current) => ({ ...current, selectedJobs: jobs })); + }; + + private removeJob = (job: Job) => { + const { jobs } = this.state; + const filtered = jobs.filter((j) => j.id !== job.id); + this.setState((current) => ({ ...current, jobs: filtered })); + }; + + private renderDeleteButton = () => { + const { selectedJobs } = this.state; + if (selectedJobs.length === 0) return undefined; + + const performDelete = async () => { + for (const job of selectedJobs) { + try { + await this.props.apiClient.deleteReport(job.id); + this.removeJob(job); + this.props.toasts.addSuccess( + i18n.translate('xpack.reporting.listing.table.deleteConfim', { + defaultMessage: `The {reportTitle} report was deleted`, + values: { + reportTitle: job.title, + }, + }) + ); + } catch (error) { + this.props.toasts.addDanger( + i18n.translate('xpack.reporting.listing.table.deleteFailedErrorMessage', { + defaultMessage: `The report was not deleted: {error}`, + values: { error }, + }) + ); + throw error; + } + } + }; + + return ( + + ); + }; + + private onTableChange = ({ page }: { page: { index: number } }) => { + const { index: pageIndex } = page; + this.setState(() => ({ page: pageIndex }), this.fetchJobs); + }; + + private fetchJobs = async () => { + // avoid page flicker when poller is updating table - only display loading screen on first load + if (this.isInitialJobsFetch) { + this.setState(() => ({ isLoading: true })); + } + + let jobs: Job[]; + let total: number; + try { + jobs = await this.props.apiClient.list(this.state.page); + total = await this.props.apiClient.total(); + this.isInitialJobsFetch = false; + } catch (fetchError) { + if (!this.licenseAllowsToShowThisPage()) { + this.props.toasts.addDanger(this.state.badLicenseMessage); + this.props.redirect('management'); + return; + } + + if (fetchError.message === 'Failed to fetch') { + this.props.toasts.addDanger( + fetchError.message || + i18n.translate('xpack.reporting.listing.table.requestFailedErrorMessage', { + defaultMessage: 'Request failed', + }) + ); + } + if (this.mounted) { + this.setState(() => ({ isLoading: false, jobs: [], total: 0 })); + } + return; + } + + if (this.mounted) { + this.setState(() => ({ + isLoading: false, + total, + jobs, + })); + } + }; + + private licenseAllowsToShowThisPage = () => { + return this.state.showLinks && this.state.enableLinks; + }; + + /** + * Widths like this are not the best, but the auto-layout does not play well with text in links. We can update + * this with something that works better on all screen sizes. This works for desktop, mobile fallback is provided on a + * per column basis. + */ + private readonly tableColumnWidths = { + type: '5%', + title: '30%', + status: '20%', + createdAt: '25%', + content: '10%', + actions: '10%', + }; + + public render() { + const { tableColumnWidths } = this; + const tableColumns: TableColumn[] = [ + { + field: 'type', + width: tableColumnWidths.type, + name: i18n.translate('xpack.reporting.listing.tableColumns.typeTitle', { + defaultMessage: 'Type', + }), + render: (_type: string, job) => { + return ( +
    + +
    + ); + }, + mobileOptions: { + show: true, + render: (job) => { + return
    {job.objectType}
    ; + }, + }, + }, + { + field: 'title', + name: i18n.translate('xpack.reporting.listing.tableColumns.reportTitle', { + defaultMessage: 'Title', + }), + width: tableColumnWidths.title, + render: (objectTitle: string, job) => { + return ( +
    + this.setState({ selectedJob: job })} + > + {objectTitle || + i18n.translate('xpack.reporting.listing.table.noTitleLabel', { + defaultMessage: 'Untitled', + })} + +
    + ); + }, + mobileOptions: { + header: false, + width: '100%', // This is not recognized by EUI types but has an effect, leaving for now + } as unknown as { header: boolean }, + }, + { + field: 'status', + width: tableColumnWidths.status, + name: i18n.translate('xpack.reporting.listing.tableColumns.statusTitle', { + defaultMessage: 'Status', + }), + render: (_status: string, job) => { + return ( + + + + ); + }, + mobileOptions: { + show: false, + }, + }, + { + field: 'created_at', + width: tableColumnWidths.createdAt, + name: i18n.translate('xpack.reporting.listing.tableColumns.createdAtTitle', { + defaultMessage: 'Created at', + }), + render: (_createdAt: string, job) => ( +
    {job.getCreatedAtDate()}
    + ), + mobileOptions: { + show: false, + }, + }, + { + field: 'content', + width: tableColumnWidths.content, + name: i18n.translate('xpack.reporting.listing.tableColumns.content', { + defaultMessage: 'Content', + }), + render: (_status: string, job) => prettyPrintJobType(job.jobtype), + mobileOptions: { + show: false, + }, + }, + { + name: i18n.translate('xpack.reporting.listing.tableColumns.actionsTitle', { + defaultMessage: 'Actions', + }), + width: tableColumnWidths.actions, + actions: [ + { + isPrimary: true, + 'data-test-subj': 'reportDownloadLink', + type: 'icon', + icon: 'download', + name: i18n.translate('xpack.reporting.listing.table.downloadReportButtonLabel', { + defaultMessage: 'Download report', + }), + description: i18n.translate('xpack.reporting.listing.table.downloadReportDescription', { + defaultMessage: 'Download this report in a new tab.', + }), + onClick: (job) => this.props.apiClient.downloadReport(job.id), + enabled: (job) => job.isDownloadReady, + }, + { + name: i18n.translate( + 'xpack.reporting.listing.table.viewReportingInfoActionButtonLabel', + { + defaultMessage: 'View report info', + } + ), + description: i18n.translate( + 'xpack.reporting.listing.table.viewReportingInfoActionButtonDescription', + { + defaultMessage: 'View additional information about this report.', + } + ), + type: 'icon', + icon: 'iInCircle', + onClick: (job) => this.setState({ selectedJob: job }), + }, + { + name: i18n.translate('xpack.reporting.listing.table.openInKibanaAppLabel', { + defaultMessage: 'Open in Kibana', + }), + 'data-test-subj': 'reportOpenInKibanaApp', + description: i18n.translate( + 'xpack.reporting.listing.table.openInKibanaAppDescription', + { + defaultMessage: 'Open the Kibana app where this report was generated.', + } + ), + available: (job) => job.canLinkToKibanaApp, + type: 'icon', + icon: 'popout', + onClick: (job) => { + const href = this.props.apiClient.getKibanaAppHref(job); + window.open(href, '_blank'); + window.focus(); + }, + }, + ], + }, + ]; + + const pagination = { + pageIndex: this.state.page, + pageSize: 10, + totalItemCount: this.state.total, + showPerPageOptions: false, + }; + + const selection = { + itemId: 'id', + onSelectionChange: this.onSelectionChange, + }; + + return ( + + {this.state.selectedJobs.length > 0 && ( +
    + + {this.renderDeleteButton()} + + +
    + )} + ({ 'data-test-subj': REPORT_TABLE_ROW_ID })} + /> + {!!this.state.selectedJob && ( + this.setState({ selectedJob: undefined })} + job={this.state.selectedJob} + /> + )} +
    + ); + } +} diff --git a/x-pack/plugins/reporting/public/management/stateful/report_listing_stateful.tsx b/x-pack/plugins/reporting/public/management/stateful/report_listing_stateful.tsx new file mode 100644 index 0000000000000..3e7a3c8cb10fd --- /dev/null +++ b/x-pack/plugins/reporting/public/management/stateful/report_listing_stateful.tsx @@ -0,0 +1,87 @@ +/* + * Copyright 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, { FC } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiPageHeader, + EuiSpacer, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { ListingPropsInternal } from '..'; +import { useIlmPolicyStatus } from '../../lib/ilm_policy_status_context'; +import { IlmPolicyLink, MigrateIlmPolicyCallOut, ReportDiagnostic } from '../components'; +import { ReportListingTable } from '../report_listing_table'; + +/** + * Used in Stateful deployments only + * Renders controls for ILM and Screenshotting Diagnostics which are only applicable in Stateful + */ +export const ReportListingStateful: FC = (props) => { + const { apiClient, capabilities, config, navigateToUrl, toasts, urlService, ...listingProps } = + props; + const ilmLocator = urlService.locators.get('ILM_LOCATOR_ID'); + const ilmPolicyContextValue = useIlmPolicyStatus(); + const hasIlmPolicy = ilmPolicyContextValue?.status !== 'policy-not-found'; + const showIlmPolicyLink = Boolean(ilmLocator && hasIlmPolicy); + return ( + <> + + } + description={ + + } + /> + + + + + + + + + + {capabilities?.management?.data?.index_lifecycle_management && ( + + {ilmPolicyContextValue?.isLoading ? ( + + ) : ( + showIlmPolicyLink && ( + + ) + )} + + )} + + + + + + ); +}; diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index 5bf73525c2dcc..bf868199de4ce 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -18,6 +18,7 @@ import { ReportingAPIClient } from '../lib/reporting_api_client'; import type { ReportingPublicPluginStartDependencies } from '../plugin'; import type { ActionContext } from './get_csv_panel_action'; import { ReportingCsvPanelAction } from './get_csv_panel_action'; +import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; const core = coreMock.createSetup(); let apiClient: ReportingAPIClient; @@ -124,7 +125,7 @@ describe('GetCsvReportPanelAction', () => { createCopy: () => mockSearchSource, removeField: jest.fn(), setField: jest.fn(), - getField: jest.fn(), + getField: jest.fn((name) => (name === 'index' ? dataViewMock : undefined)), getSerializedFields: jest.fn().mockImplementation(() => ({ testData: 'testDataValue' })), } as unknown as SearchSource; context.embeddable.getSavedSearch = () => { diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts index ddd226187adf0..a88abae999be0 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts @@ -5,12 +5,9 @@ * 2.0. */ -import { promisify } from 'util'; - import { schema, TypeOf } from '@kbn/config-schema'; import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; import { ALLOWED_JOB_CONTENT_TYPES } from '@kbn/reporting-common'; - import { getCounters } from '..'; import { ReportingCore } from '../../..'; import { getContentStream } from '../../../lib'; @@ -84,9 +81,12 @@ export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { const docIndex = doc.index; const stream = await getContentStream(reporting, { id: docId, index: docIndex }); - /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ - await promisify(stream.end.bind(stream, '', 'utf8'))(); + await new Promise((resolve) => { + stream.end('', 'utf8', () => { + resolve(); + }); + }); await jobsQuery.delete(docIndex, docId); return res.ok({ diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts index 9efe74a0c3aac..b87c6040e46b4 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts @@ -206,11 +206,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory async delete(deleteIndex, id) { try { const { asInternalUser: elasticsearchClient } = await reportingCore.getEsClient(); - - // Using `wait_for` helps avoid users seeing recently-deleted reports temporarily flashing back in the - // job listing. - const query = { id, index: deleteIndex, refresh: 'wait_for' as const }; - + const query = { id, index: deleteIndex }; return await elasticsearchClient.delete(query, { meta: true }); } catch (error) { throw new Error( diff --git a/x-pack/plugins/reporting/tsconfig.json b/x-pack/plugins/reporting/tsconfig.json index 53a4ab34eb197..4b784e29db102 100644 --- a/x-pack/plugins/reporting/tsconfig.json +++ b/x-pack/plugins/reporting/tsconfig.json @@ -50,6 +50,7 @@ "@kbn/reporting-mocks-server", "@kbn/core-http-request-handler-context-server", "@kbn/reporting-public", + "@kbn/discover-utils", ], "exclude": [ "target/**/*", 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 4d25b41b6db0e..be24975deae6f 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 @@ -79,6 +79,13 @@ it('matches snapshot', () => { }, "kibana.alert.reason": Object { "array": false, + "multi_fields": Array [ + Object { + "flat_name": "kibana.alert.reason.text", + "name": "text", + "type": "match_only_text", + }, + ], "required": false, "type": "keyword", }, diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index 47eeb6e46c406..f395ddb4b75f7 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -22,6 +22,7 @@ import { ALERT_STATUS_ACTIVE, ALERT_CASE_IDS, MAX_CASES_PER_ALERT, + AlertConsumers, } from '@kbn/rule-data-utils'; import { @@ -80,6 +81,7 @@ export interface ConstructorOptions { esClient: ElasticsearchClient; ruleDataService: IRuleDataService; getRuleType: RuleTypeRegistry['get']; + getRuleList: RuleTypeRegistry['list']; getAlertIndicesAlias: AlertingStart['getAlertIndicesAlias']; } @@ -153,6 +155,7 @@ export class AlertsClient { private readonly spaceId: string | undefined; private readonly ruleDataService: IRuleDataService; private readonly getRuleType: RuleTypeRegistry['get']; + private readonly getRuleList: RuleTypeRegistry['list']; private getAlertIndicesAlias!: AlertingStart['getAlertIndicesAlias']; constructor(options: ConstructorOptions) { @@ -165,6 +168,7 @@ export class AlertsClient { this.spaceId = this.authorization.getSpaceId(); this.ruleDataService = options.ruleDataService; this.getRuleType = options.getRuleType; + this.getRuleList = options.getRuleList; this.getAlertIndicesAlias = options.getAlertIndicesAlias; } @@ -1076,19 +1080,31 @@ export class AlertsClient { } public async getBrowserFields({ + featureIds, indices, metaFields, allowNoIndex, }: { + featureIds: string[]; indices: string[]; metaFields: string[]; allowNoIndex: boolean; }): Promise<{ browserFields: BrowserFields; fields: FieldDescriptor[] }> { const indexPatternsFetcherAsInternalUser = new IndexPatternsFetcher(this.esClient); + const ruleTypeList = this.getRuleList(); + const fieldsForAAD = new Set(); + for (const rule of ruleTypeList) { + if (featureIds.includes(rule.producer) && rule.hasFieldsForAAD) { + (rule.fieldsForAAD ?? []).forEach((f) => { + fieldsForAAD.add(f); + }); + } + } const { fields } = await indexPatternsFetcherAsInternalUser.getFieldsForWildcard({ pattern: indices, metaFields, fieldCapsOptions: { allow_no_indices: allowNoIndex }, + fields: [...fieldsForAAD, 'kibana.*'], }); return { @@ -1099,11 +1115,13 @@ export class AlertsClient { public async getAADFields({ ruleTypeId }: { ruleTypeId: string }) { const { producer, fieldsForAAD = [] } = this.getRuleType(ruleTypeId); + if (producer === AlertConsumers.SIEM) { + throw Boom.badRequest(`Security solution rule type is not supported`); + } const indices = await this.getAuthorizedAlertsIndices([producer]); - const o11yIndices = indices?.filter((index) => index.startsWith('.alerts-observability')) ?? []; const indexPatternsFetcherAsInternalUser = new IndexPatternsFetcher(this.esClient); - const { fields } = await indexPatternsFetcherAsInternalUser.getFieldsForWildcard({ - pattern: o11yIndices, + const { fields = [] } = await indexPatternsFetcherAsInternalUser.getFieldsForWildcard({ + pattern: indices ?? [], metaFields: ['_id', '_index'], fieldCapsOptions: { allow_no_indices: true }, fields: [...fieldsForAAD, 'kibana.*'], diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts index 43966d1207004..367ead5744d55 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts @@ -26,6 +26,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = { esClient: {} as ElasticsearchClient, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; @@ -53,6 +54,7 @@ describe('AlertsClientFactory', () => { auditLogger, esClient: {}, ruleDataService: alertsClientFactoryParams.ruleDataService, + getRuleList: alertsClientFactoryParams.getRuleList, getRuleType: alertsClientFactoryParams.getRuleType, getAlertIndicesAlias: alertsClientFactoryParams.getAlertIndicesAlias, }); diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts index de0afb5a0b226..934074cc4a2ed 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts @@ -23,6 +23,7 @@ export interface AlertsClientFactoryProps { securityPluginSetup: SecurityPluginSetup | undefined; ruleDataService: IRuleDataService | null; getRuleType: RuleTypeRegistry['get']; + getRuleList: RuleTypeRegistry['list']; getAlertIndicesAlias: AlertingStart['getAlertIndicesAlias']; } @@ -36,6 +37,7 @@ export class AlertsClientFactory { private securityPluginSetup!: SecurityPluginSetup | undefined; private ruleDataService!: IRuleDataService | null; private getRuleType!: RuleTypeRegistry['get']; + private getRuleList!: RuleTypeRegistry['list']; private getAlertIndicesAlias!: AlertingStart['getAlertIndicesAlias']; public initialize(options: AlertsClientFactoryProps) { @@ -53,6 +55,7 @@ export class AlertsClientFactory { this.securityPluginSetup = options.securityPluginSetup; this.ruleDataService = options.ruleDataService; this.getRuleType = options.getRuleType; + this.getRuleList = options.getRuleList; this.getAlertIndicesAlias = options.getAlertIndicesAlias; } @@ -66,6 +69,7 @@ export class AlertsClientFactory { esClient: this.esClient, ruleDataService: this.ruleDataService!, getRuleType: this.getRuleType, + getRuleList: this.getRuleList, getAlertIndicesAlias: this.getAlertIndicesAlias, }); } diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts index 4229ae23793fc..28cd76ca6dffe 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts @@ -31,6 +31,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts index 4047a3ecadd27..544fab479f9dd 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update_cases.test.ts @@ -37,6 +37,7 @@ describe('bulkUpdateCases', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts index 37ad46a523a70..8ccae88dd83c3 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts @@ -30,6 +30,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts index fb1e0eef432ef..4185fb7e83eb6 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts @@ -31,6 +31,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get_aad_fields.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get_aad_fields.test.ts new file mode 100644 index 0000000000000..777b3d3e26742 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get_aad_fields.test.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 { AlertConsumers } from '@kbn/rule-data-utils'; +import { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { alertingAuthorizationMock } from '@kbn/alerting-plugin/server/authorization/alerting_authorization.mock'; +import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; + +const alertingAuthMock = alertingAuthorizationMock.create(); +const esClientMock = elasticsearchClientMock.createElasticsearchClient(); +const auditLogger = auditLoggerMock.create(); +const getRuleTypeMock = jest.fn(); +const alertsClientParams: jest.Mocked = { + logger: loggingSystemMock.create().get(), + authorization: alertingAuthMock, + esClient: esClientMock, + auditLogger, + ruleDataService: ruleDataServiceMock.create(), + getRuleType: getRuleTypeMock, + getRuleList: jest.fn(), + getAlertIndicesAlias: jest.fn(), +}; + +const DEFAULT_SPACE = 'test_default_space_id'; + +beforeEach(() => { + jest.resetAllMocks(); + alertingAuthMock.getSpaceId.mockImplementation(() => DEFAULT_SPACE); +}); + +describe('getAADFields()', () => { + test('should throw an error when a rule type belong to security solution', async () => { + getRuleTypeMock.mockImplementation(() => ({ + producer: AlertConsumers.SIEM, + fieldsForAAD: [], + })); + const alertsClient = new AlertsClient(alertsClientParams); + + await expect( + alertsClient.getAADFields({ ruleTypeId: 'security-type' }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Security solution rule type is not supported"`); + }); +}); diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts index 2611200afd85f..317de5d52e8e2 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/remove_cases_from_alerts.test.ts @@ -32,6 +32,7 @@ describe('remove cases from alerts', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; @@ -90,6 +91,7 @@ describe('remove cases from alerts', () => { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts index bca5e7d967f3a..bd6a1b2695cd1 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts @@ -30,6 +30,7 @@ const alertsClientParams: jest.Mocked = { auditLogger, ruleDataService: ruleDataServiceMock.create(), getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: jest.fn(), }; diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 6fba837a10c1a..8cb6df23ae766 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -166,6 +166,7 @@ export class RuleRegistryPlugin securityPluginSetup: security, ruleDataService, getRuleType: plugins.alerting.getType, + getRuleList: plugins.alerting.listTypes, getAlertIndicesAlias: plugins.alerting.getAlertIndicesAlias, }); diff --git a/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts b/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts index 259ca03478745..995f992e0b800 100644 --- a/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts +++ b/x-pack/plugins/rule_registry/server/routes/get_browser_fields_by_feature_id.ts @@ -53,6 +53,7 @@ export const getBrowserFieldsByFeatureId = (router: IRouter { expect(ruleDataClient.isWriteEnabled()).toBe(true); }); + test('sanitizes error before logging', async () => { + scopedClusterClient.bulk.mockResponseOnce({ + took: 486, + errors: true, + items: [ + { + create: { + _index: 'test', + _id: '3', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + { + create: { + _index: 'test', + _id: '4', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + { + create: { + _index: 'index1', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'. Preview of field's value: 'we don't want this field value to be echoed'", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, + ], + }); + const ruleDataClient = new RuleDataClient( + getRuleDataClientOptions({ isUsingDataStreams }) + ); + expect(ruleDataClient.isWriteEnabled()).toBe(true); + const writer = await ruleDataClient.getWriter(); + + // Previously, a delay between calling getWriter() and using a writer function + // would cause an Unhandled promise rejection if there were any errors getting a writer + // Adding this delay in the tests to ensure this does not pop up again. + await delay(); + + const bulkWriteResponse = await writer.bulk({}); + expect(bulkWriteResponse).toEqual({ + body: { + took: 486, + errors: true, + items: [ + { + create: { + _index: 'test', + _id: '3', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + { + create: { + _index: 'test', + _id: '4', + _version: 1, + result: 'created', + _shards: { total: 2, successful: 1, failed: 0 }, + status: 201, + _seq_no: 2, + _primary_term: 3, + }, + }, + { + create: { + _index: 'index1', + _id: '7', + status: 404, + error: { + type: 'mapper_parsing_exception', + reason: + "failed to parse field [process.command_line] of type [wildcard] in document with id 'f0c9805be95fedbc3c99c663f7f02cc15826c122'.", + caused_by: { + type: 'illegal_state_exception', + reason: "Can't get text on a START_OBJECT at 1:3845", + }, + }, + }, + }, + ], + }, + headers: { + 'x-elastic-product': 'Elasticsearch', + }, + meta: {}, + statusCode: 200, + warnings: [], + }); + + expect(logger.error).toHaveBeenNthCalledWith( + 1, + // @ts-expect-error + new errors.ResponseError(bulkWriteResponse) + ); + expect(ruleDataClient.isWriteEnabled()).toBe(true); + }); + test('waits until cluster client is ready before calling bulk', async () => { scopedClusterClient.bulk.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts index b4d029f4bbe82..329c060426093 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { errors } from '@elastic/elasticsearch'; +import { errors, TransportResult } from '@elastic/elasticsearch'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Either, isLeft } from 'fp-ts/lib/Either'; @@ -14,6 +14,7 @@ import { Logger } from '@kbn/core/server'; import { IndexPatternsFetcher } from '@kbn/data-plugin/server'; import type { ESSearchRequest, ESSearchResponse } from '@kbn/es-types'; +import { sanitizeBulkErrorResponse } from '@kbn/alerting-plugin/server'; import { RuleDataWriteDisabledError, RuleDataWriterInitializationError, @@ -231,13 +232,20 @@ export class RuleDataClient implements IRuleDataClient { meta: true, }); + if (!response.body.errors) { + return response; + } + // TODO: #160572 - add support for version conflict errors, in case alert was updated // some other way between the time it was fetched and the time it was updated. - if (response.body.errors) { - const error = new errors.ResponseError(response); - this.options.logger.error(error); - } - return response; + // Redact part of reason message that echoes back value + const sanitizedResponse = sanitizeBulkErrorResponse(response) as TransportResult< + estypes.BulkResponse, + unknown + >; + const error = new errors.ResponseError(sanitizedResponse); + this.options.logger.error(error); + return sanitizedResponse; } else { this.options.logger.debug(`Writing is disabled, bulk() will not write any data.`); } diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index 9aff47c459a72..48a0202105965 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -15,7 +15,6 @@ export const ALL_SPACES_ID = '*'; */ export const UNKNOWN_SPACE = '?'; -export const GLOBAL_RESOURCE = '*'; export const APPLICATION_PREFIX = 'kibana-'; /** diff --git a/x-pack/plugins/security/common/index.ts b/x-pack/plugins/security/common/index.ts index c547833949ded..e30fff0a8e76f 100644 --- a/x-pack/plugins/security/common/index.ts +++ b/x-pack/plugins/security/common/index.ts @@ -5,27 +5,40 @@ * 2.0. */ -export type { SecurityLicense, SecurityLicenseFeatures, LoginLayout } from './licensing'; export type { - AuthenticatedUser, GetUserProfileResponse, + ApiKey, + RestApiKey, + GetUserDisplayNameParams, + EditUser, + BuiltinESPrivileges, + RawKibanaPrivileges, + RoleMapping, + RoleTemplate, + StoredRoleTemplate, + InvalidRoleTemplate, + InlineRoleTemplate, +} from './model'; +export { getUserDisplayName, isRoleReserved } from './model'; + +// Re-export types from the plugin directly to enhance the developer experience for consumers of the Security plugin. +export type { + AuthenticatedUser, + UserRealm, + User, AuthenticationProvider, - PrivilegeDeprecationsService, - PrivilegeDeprecationsRolesByFeatureIdRequest, - PrivilegeDeprecationsRolesByFeatureIdResponse, Role, RoleIndexPrivilege, RoleKibanaPrivilege, + RoleRemoteIndexPrivilege, FeaturesPrivileges, - User, + LoginLayout, + SecurityLicenseFeatures, + SecurityLicense, UserProfile, UserProfileUserInfo, UserProfileWithSecurity, UserProfileData, UserProfileLabels, UserProfileUserInfoWithSecurity, - ApiKey, - UserRealm, - GetUserDisplayNameParams, -} from './model'; -export { getUserDisplayName } from './model'; +} from '@kbn/security-plugin-types-common'; diff --git a/x-pack/plugins/security/common/licensing/index.mock.ts b/x-pack/plugins/security/common/licensing/index.mock.ts index b947fa5b1ed26..7065deb939c1f 100644 --- a/x-pack/plugins/security/common/licensing/index.mock.ts +++ b/x-pack/plugins/security/common/licensing/index.mock.ts @@ -9,9 +9,7 @@ import { Observable, of } from 'rxjs'; import type { LicenseType } from '@kbn/licensing-plugin/common/types'; import { LICENSE_TYPE } from '@kbn/licensing-plugin/common/types'; - -import type { SecurityLicenseFeatures } from './license_features'; -import type { SecurityLicense } from './license_service'; +import type { SecurityLicense, SecurityLicenseFeatures } from '@kbn/security-plugin-types-common'; export const licenseMock = { create: ( diff --git a/x-pack/plugins/security/common/licensing/index.ts b/x-pack/plugins/security/common/licensing/index.ts index 48329aeb99925..e36a22b5f8b68 100644 --- a/x-pack/plugins/security/common/licensing/index.ts +++ b/x-pack/plugins/security/common/licensing/index.ts @@ -5,7 +5,4 @@ * 2.0. */ -export type { SecurityLicense } from './license_service'; export { SecurityLicenseService } from './license_service'; - -export type { LoginLayout, SecurityLicenseFeatures } from './license_features'; diff --git a/x-pack/plugins/security/common/licensing/license_service.ts b/x-pack/plugins/security/common/licensing/license_service.ts index cbb658dde9888..4372fa918e492 100644 --- a/x-pack/plugins/security/common/licensing/license_service.ts +++ b/x-pack/plugins/security/common/licensing/license_service.ts @@ -9,17 +9,7 @@ import type { Observable, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; import type { ILicense, LicenseType } from '@kbn/licensing-plugin/common/types'; - -import type { SecurityLicenseFeatures } from './license_features'; - -export interface SecurityLicense { - isLicenseAvailable(): boolean; - isEnabled(): boolean; - getFeatures(): SecurityLicenseFeatures; - hasAtLeast(licenseType: LicenseType): boolean | undefined; - features$: Observable; -} - +import type { SecurityLicenseFeatures } from '@kbn/security-plugin-types-common'; interface SetupDeps { license$: Observable; } diff --git a/x-pack/plugins/security/common/login_state.ts b/x-pack/plugins/security/common/login_state.ts index fe2c6380db3ee..b274883d0146e 100644 --- a/x-pack/plugins/security/common/login_state.ts +++ b/x-pack/plugins/security/common/login_state.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { LoginLayout } from './licensing'; +import type { LoginLayout } from '@kbn/security-plugin-types-common'; export interface LoginSelectorProvider { type: string; diff --git a/x-pack/plugins/security/common/model/authenticated_user.mock.ts b/x-pack/plugins/security/common/model/authenticated_user.mock.ts index 84b300d5c982b..6f691579b073b 100644 --- a/x-pack/plugins/security/common/model/authenticated_user.mock.ts +++ b/x-pack/plugins/security/common/model/authenticated_user.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuthenticatedUser } from './authenticated_user'; +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; // We omit `roles` here since the original interface defines this field as `readonly string[]` that makes it hard to use // in various mocks that expect mutable string array. diff --git a/x-pack/plugins/security/common/model/authenticated_user.test.ts b/x-pack/plugins/security/common/model/authenticated_user.test.ts index 4c84a951bf729..9ed02c57841bf 100644 --- a/x-pack/plugins/security/common/model/authenticated_user.test.ts +++ b/x-pack/plugins/security/common/model/authenticated_user.test.ts @@ -6,8 +6,8 @@ */ import { applicationServiceMock } from '@kbn/core/public/mocks'; +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; -import type { AuthenticatedUser } from './authenticated_user'; import { canUserChangeDetails, canUserChangePassword, diff --git a/x-pack/plugins/security/common/model/authenticated_user.ts b/x-pack/plugins/security/common/model/authenticated_user.ts index fd78b250a5ccc..02c1e0f3936dc 100644 --- a/x-pack/plugins/security/common/model/authenticated_user.ts +++ b/x-pack/plugins/security/common/model/authenticated_user.ts @@ -6,64 +6,10 @@ */ import type { Capabilities } from '@kbn/core/types'; - -import type { AuthenticationProvider } from './authentication_provider'; -import type { User } from './user'; +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; const REALMS_ELIGIBLE_FOR_PASSWORD_CHANGE = ['reserved', 'native']; -/** - * An Elasticsearch realm that was used to resolve and authenticate the user. - */ -export interface UserRealm { - /** - * Arbitrary name of the security realm. - */ - name: string; - - /** - * Type of the security realm (file, native, saml etc.). - */ - type: string; -} - -/** - * Represents the currently authenticated user. - */ -export interface AuthenticatedUser extends User { - /** - * The name and type of the Realm that has authenticated the user. - */ - authentication_realm: UserRealm; - - /** - * The name and type of the Realm where the user information were retrieved from. - */ - lookup_realm: UserRealm; - - /** - * The authentication provider that used to authenticate user. - */ - authentication_provider: AuthenticationProvider; - - /** - * The AuthenticationType used by ES to authenticate the user. - * - * @example "realm" | "api_key" | "token" | "anonymous" | "internal" - */ - authentication_type: string; - - /** - * Indicates whether user is authenticated via Elastic Cloud built-in SAML realm. - */ - elastic_cloud_user: boolean; - - /** - * User profile ID of this user. - */ - profile_uid?: string; -} - export function isUserAnonymous(user: Pick) { return user.authentication_provider.type === 'anonymous'; } diff --git a/x-pack/plugins/security/common/model/authentication_provider.ts b/x-pack/plugins/security/common/model/authentication_provider.ts index 9435c8ec55c95..4cdc49cf62ee6 100644 --- a/x-pack/plugins/security/common/model/authentication_provider.ts +++ b/x-pack/plugins/security/common/model/authentication_provider.ts @@ -5,20 +5,6 @@ * 2.0. */ -/** - * Type and name tuple to identify provider used to authenticate user. - */ -export interface AuthenticationProvider { - /** - * Type of the Kibana authentication provider. - */ - type: string; - /** - * Name of the Kibana authentication provider (arbitrary string). - */ - name: string; -} - /** * Checks whether authentication provider with the specified type uses Kibana's native login form. * @param providerType Type of the authentication provider. diff --git a/x-pack/plugins/security/common/model/index.ts b/x-pack/plugins/security/common/model/index.ts index c8505a644503f..006a0104e30d1 100644 --- a/x-pack/plugins/security/common/model/index.ts +++ b/x-pack/plugins/security/common/model/index.ts @@ -13,40 +13,23 @@ export type { ApiKeyRoleDescriptors, CrossClusterApiKeyAccess, } from './api_key'; -export type { User, EditUser, GetUserDisplayNameParams } from './user'; -export type { - GetUserProfileResponse, - UserProfile, - UserProfileUserInfo, - UserProfileWithSecurity, - UserProfileData, - UserProfileLabels, - UserProfileUserInfoWithSecurity, -} from './user_profile'; +export type { EditUser, GetUserDisplayNameParams } from './user'; +export type { GetUserProfileResponse } from './user_profile'; export { getUserAvatarColor, getUserAvatarInitials, USER_AVATAR_MAX_INITIALS, } from './user_profile'; export { getUserDisplayName } from './user'; -export type { AuthenticatedUser, UserRealm } from './authenticated_user'; export { canUserChangePassword, canUserChangeDetails, isUserAnonymous, canUserHaveProfile, } from './authenticated_user'; -export type { AuthenticationProvider } from './authentication_provider'; export { shouldProviderUseLoginForm } from './authentication_provider'; export type { BuiltinESPrivileges } from './builtin_es_privileges'; export type { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_privileges'; -export type { FeaturesPrivileges } from './features_privileges'; -export type { - Role, - RoleIndexPrivilege, - RoleRemoteIndexPrivilege, - RoleKibanaPrivilege, -} from './role'; export { copyRole, isRoleDeprecated, @@ -65,8 +48,3 @@ export type { RoleTemplate, RoleMapping, } from './role_mapping'; -export type { - PrivilegeDeprecationsRolesByFeatureIdRequest, - PrivilegeDeprecationsRolesByFeatureIdResponse, - PrivilegeDeprecationsService, -} from './deprecations'; diff --git a/x-pack/plugins/security/common/model/role.test.ts b/x-pack/plugins/security/common/model/role.test.ts index 0aa34fefc73e5..b973115ed69b4 100644 --- a/x-pack/plugins/security/common/model/role.test.ts +++ b/x-pack/plugins/security/common/model/role.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { Role } from '.'; +import type { Role } from '@kbn/security-plugin-types-common'; + import { copyRole, getExtendedRoleDeprecationNotice, diff --git a/x-pack/plugins/security/common/model/role.ts b/x-pack/plugins/security/common/model/role.ts index 17971905fc1ed..1872f3bff2f5a 100644 --- a/x-pack/plugins/security/common/model/role.ts +++ b/x-pack/plugins/security/common/model/role.ts @@ -8,48 +8,7 @@ import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; - -import type { FeaturesPrivileges } from './features_privileges'; - -export interface RoleIndexPrivilege { - names: string[]; - privileges: string[]; - field_security?: { - grant?: string[]; - except?: string[]; - }; - query?: string; -} - -export interface RoleRemoteIndexPrivilege extends RoleIndexPrivilege { - clusters: string[]; -} - -export interface RoleKibanaPrivilege { - spaces: string[]; - base: string[]; - feature: FeaturesPrivileges; - _reserved?: string[]; -} - -export interface Role { - name: string; - elasticsearch: { - cluster: string[]; - indices: RoleIndexPrivilege[]; - remote_indices?: RoleRemoteIndexPrivilege[]; - run_as: string[]; - }; - kibana: RoleKibanaPrivilege[]; - metadata?: { - [anyKey: string]: any; - }; - transient_metadata?: { - [anyKey: string]: any; - }; - _transform_error?: string[]; - _unrecognized_applications?: string[]; -} +import type { Role } from '@kbn/security-plugin-types-common'; /** * Returns whether given role is enabled or not diff --git a/x-pack/plugins/security/common/model/user.test.ts b/x-pack/plugins/security/common/model/user.test.ts index 80a2abcf40a04..b22344ea8e233 100644 --- a/x-pack/plugins/security/common/model/user.test.ts +++ b/x-pack/plugins/security/common/model/user.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { User } from './user'; +import type { User } from '@kbn/security-plugin-types-common'; + import { getUserDisplayName } from './user'; describe('#getUserDisplayName', () => { diff --git a/x-pack/plugins/security/common/model/user.ts b/x-pack/plugins/security/common/model/user.ts index 88bbf378c508c..a04e9be77dfcb 100644 --- a/x-pack/plugins/security/common/model/user.ts +++ b/x-pack/plugins/security/common/model/user.ts @@ -5,21 +5,7 @@ * 2.0. */ -/** - * A set of fields describing Kibana user. - */ -export interface User { - username: string; - email?: string; - full_name?: string; - roles: readonly string[]; - enabled: boolean; - metadata?: { - _reserved: boolean; - _deprecated?: boolean; - _deprecated_reason?: string; - }; -} +import type { User } from '@kbn/security-plugin-types-common'; export interface EditUser extends User { password?: string; diff --git a/x-pack/plugins/security/common/model/user_profile.mock.ts b/x-pack/plugins/security/common/model/user_profile.mock.ts index 7e72ce26e13c9..519948b94f578 100644 --- a/x-pack/plugins/security/common/model/user_profile.mock.ts +++ b/x-pack/plugins/security/common/model/user_profile.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { UserProfile, UserProfileWithSecurity } from './user_profile'; +import type { UserProfile, UserProfileWithSecurity } from '@kbn/security-plugin-types-common'; function createUserProfileMock(userProfile: Partial = {}) { return { diff --git a/x-pack/plugins/security/common/model/user_profile.ts b/x-pack/plugins/security/common/model/user_profile.ts index 152b0d0266bbe..0c9daba622e70 100644 --- a/x-pack/plugins/security/common/model/user_profile.ts +++ b/x-pack/plugins/security/common/model/user_profile.ts @@ -7,111 +7,16 @@ import { VISUALIZATION_COLORS } from '@elastic/eui'; +import type { + AuthenticatedUser, + UserProfileData, + UserProfileUserInfo, + UserProfileWithSecurity, +} from '@kbn/security-plugin-types-common'; import type { UserProfileAvatarData } from '@kbn/user-profile-components'; -import type { AuthenticatedUser } from './authenticated_user'; import { getUserDisplayName } from './user'; -/** - * IMPORTANT: - * - * The types in this file are duplicated at - * `packages/kbn-user-profile-components/src/user_profile.ts` - * - * When making changes please ensure to keep both files in sync. - */ - -/** - * Describes basic properties stored in user profile. - */ -export interface UserProfile { - /** - * Unique ID for of the user profile. - */ - uid: string; - - /** - * Indicates whether user profile is enabled or not. - */ - enabled: boolean; - - /** - * Information about the user that owns profile. - */ - user: UserProfileUserInfo; - - /** - * User specific data associated with the profile. - */ - data: Partial; -} - -/** - * Basic user information returned in user profile. - */ -export interface UserProfileUserInfo { - /** - * Username of the user. - */ - username: string; - /** - * Optional email of the user. - */ - email?: string; - /** - * Optional full name of the user. - */ - full_name?: string; -} - -/** - * Placeholder for data stored in user profile. - */ -export type UserProfileData = Record; - -/** - * Type of the user profile labels structure (currently - */ -export type UserProfileLabels = Record; - -/** - * Extended user information returned in user profile (both basic and security related properties). - */ -export interface UserProfileUserInfoWithSecurity extends UserProfileUserInfo { - /** - * List of the user roles. - */ - roles: readonly string[]; - /** - * Name of the Elasticsearch security realm that was used to authenticate user. - */ - realm_name: string; - /** - * Optional name of the security domain that Elasticsearch security realm that was - * used to authenticate user resides in (if any). - */ - realm_domain?: string; -} - -/** - * Describes all properties stored in user profile (both basic and security related properties). - */ -export interface UserProfileWithSecurity< - D extends UserProfileData = UserProfileData, - L extends UserProfileLabels = UserProfileLabels -> extends UserProfile { - /** - * Information about the user that owns profile. - */ - user: UserProfileUserInfoWithSecurity; - - /** - * User specific _searchable_ labels associated with the profile. Note that labels are considered - * security related field since it's going to be used to store user's space ID. - */ - labels: L; -} - /** * User profile enriched with session information. */ diff --git a/x-pack/plugins/security/common/types.ts b/x-pack/plugins/security/common/types.ts index 65616e58e65b2..1fc47aad365de 100644 --- a/x-pack/plugins/security/common/types.ts +++ b/x-pack/plugins/security/common/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuthenticationProvider } from './model'; +import type { AuthenticationProvider } from '@kbn/security-plugin-types-common'; export interface SessionInfo { expiresInMs: number | null; diff --git a/x-pack/plugins/security/public/account_management/account_management_app.tsx b/x-pack/plugins/security/public/account_management/account_management_app.tsx index a5b98d66d46ff..59678a246abe6 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_app.tsx @@ -26,10 +26,10 @@ import { KibanaThemeProvider, toMountPoint, } from '@kbn/kibana-react-plugin/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { Router } from '@kbn/shared-ux-router'; import { UserProfilesKibanaProvider } from '@kbn/user-profile-components'; -import type { AuthenticationServiceSetup } from '../authentication'; import type { SecurityApiClients } from '../components'; import { AuthenticationProvider, SecurityApiClientsProvider } from '../components'; import type { BreadcrumbsChangeHandler } from '../components/breadcrumb'; diff --git a/x-pack/plugins/security/public/account_management/index.ts b/x-pack/plugins/security/public/account_management/index.ts index e1a4957aa71e7..966de2cb05b89 100644 --- a/x-pack/plugins/security/public/account_management/index.ts +++ b/x-pack/plugins/security/public/account_management/index.ts @@ -7,8 +7,3 @@ export { accountManagementApp } from './account_management_app'; export { UserProfileAPIClient } from './user_profile/user_profile_api_client'; -export type { - UserProfileBulkGetParams, - UserProfileGetCurrentParams, - UserProfileSuggestParams, -} from './user_profile'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/index.ts b/x-pack/plugins/security/public/account_management/user_profile/index.ts index ed34d7d4a4339..3ce9e1919040d 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/index.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/index.ts @@ -8,8 +8,3 @@ export { UserProfile } from './user_profile'; export type { UserProfileProps, UserProfileFormValues } from './user_profile'; -export type { - UserProfileGetCurrentParams, - UserProfileBulkGetParams, - UserProfileSuggestParams, -} from './user_profile_api_client'; diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts index 4760aa15ab0b3..597b93236a2a5 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile_api_client.ts @@ -10,60 +10,17 @@ import type { Observable } from 'rxjs'; import { BehaviorSubject, Subject } from 'rxjs'; import type { HttpStart } from '@kbn/core/public'; +import type { + UserProfileAPIClient as UserProfileAPIClientType, + UserProfileBulkGetParams, + UserProfileGetCurrentParams, + UserProfileSuggestParams, +} from '@kbn/security-plugin-types-public'; import type { UserProfileData } from '@kbn/user-profile-components'; import type { GetUserProfileResponse, UserProfile } from '../../../common'; -/** - * Parameters for the get user profile for the current user API. - */ -export interface UserProfileGetCurrentParams { - /** - * By default, get API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath: string; -} - -/** - * Parameters for the bulk get API. - */ -export interface UserProfileBulkGetParams { - /** - * List of user profile identifiers. - */ - uids: Set; - - /** - * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath?: string; -} - -/** - * Parameters for the suggest API. - */ -export interface UserProfileSuggestParams { - /** - * Query string used to match name-related fields in user profiles. The following fields are treated as - * name-related: username, full_name and email. - */ - name: string; - - /** - * Desired number of suggestions to return. The default value is 10. - */ - size?: number; - - /** - * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath?: string; -} - -export class UserProfileAPIClient { +export class UserProfileAPIClient implements UserProfileAPIClientType { private readonly internalDataUpdates$: Subject = new Subject(); /** diff --git a/x-pack/plugins/security/public/analytics/analytics_service.ts b/x-pack/plugins/security/public/analytics/analytics_service.ts index 33a72f24c487b..8d4e173246ffd 100644 --- a/x-pack/plugins/security/public/analytics/analytics_service.ts +++ b/x-pack/plugins/security/public/analytics/analytics_service.ts @@ -14,9 +14,9 @@ import type { HttpSetup, HttpStart, } from '@kbn/core/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { registerUserContext } from './register_user_context'; -import type { AuthenticationServiceSetup } from '..'; import type { SecurityLicense } from '../../common'; interface AnalyticsServiceSetupParams { diff --git a/x-pack/plugins/security/public/analytics/register_user_context.test.ts b/x-pack/plugins/security/public/analytics/register_user_context.test.ts index bc4e0dd093835..8ffbde29bb940 100644 --- a/x-pack/plugins/security/public/analytics/register_user_context.test.ts +++ b/x-pack/plugins/security/public/analytics/register_user_context.test.ts @@ -10,9 +10,9 @@ import { firstValueFrom } from 'rxjs'; import type { AnalyticsServiceSetup } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; import { Sha256 } from '@kbn/crypto-browser'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { registerUserContext } from './register_user_context'; -import type { AuthenticationServiceSetup } from '..'; import { authenticationMock } from '../authentication/index.mock'; import { securityMock } from '../mocks'; diff --git a/x-pack/plugins/security/public/analytics/register_user_context.ts b/x-pack/plugins/security/public/analytics/register_user_context.ts index 19ecf0a6896fa..e4464df8196a8 100644 --- a/x-pack/plugins/security/public/analytics/register_user_context.ts +++ b/x-pack/plugins/security/public/analytics/register_user_context.ts @@ -9,8 +9,7 @@ import { catchError, from, map, of } from 'rxjs'; import type { AnalyticsServiceSetup } from '@kbn/core/public'; import { Sha256 } from '@kbn/crypto-browser'; - -import type { AuthenticationServiceSetup } from '..'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; interface UserIdContext { userId?: string; diff --git a/x-pack/plugins/security/public/authentication/authentication_service.ts b/x-pack/plugins/security/public/authentication/authentication_service.ts index 62c60587282ef..dcc0588e4bf33 100644 --- a/x-pack/plugins/security/public/authentication/authentication_service.ts +++ b/x-pack/plugins/security/public/authentication/authentication_service.ts @@ -11,6 +11,7 @@ import type { HttpSetup, StartServicesAccessor, } from '@kbn/core/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { accessAgreementApp } from './access_agreement'; import { captureURLApp } from './capture_url'; @@ -18,7 +19,7 @@ import { loggedOutApp } from './logged_out'; import { loginApp } from './login'; import { logoutApp } from './logout'; import { overwrittenSessionApp } from './overwritten_session'; -import type { AuthenticatedUser } from '../../common/model'; +import type { AuthenticatedUser } from '../../common'; import type { ConfigType } from '../config'; import type { PluginStartDependencies } from '../plugin'; @@ -29,24 +30,6 @@ interface SetupParams { http: HttpSetup; getStartServices: StartServicesAccessor; } - -export interface AuthenticationServiceSetup { - /** - * Returns currently authenticated user and throws if current user isn't authenticated. - */ - getCurrentUser: () => Promise; - - /** - * Determines if API Keys are currently enabled. - */ - areAPIKeysEnabled: () => Promise; -} - -/** - * Start has the same contract as Setup for now. - */ -export type AuthenticationServiceStart = AuthenticationServiceSetup; - export class AuthenticationService { public setup({ application, diff --git a/x-pack/plugins/security/public/authentication/index.mock.ts b/x-pack/plugins/security/public/authentication/index.mock.ts index 092126e6cfeed..cc1c098eb6794 100644 --- a/x-pack/plugins/security/public/authentication/index.mock.ts +++ b/x-pack/plugins/security/public/authentication/index.mock.ts @@ -8,7 +8,7 @@ import type { AuthenticationServiceSetup, AuthenticationServiceStart, -} from './authentication_service'; +} from '@kbn/security-plugin-types-public'; export const authenticationMock = { createSetup: (): jest.Mocked => ({ diff --git a/x-pack/plugins/security/public/authentication/index.ts b/x-pack/plugins/security/public/authentication/index.ts index dd7cb006d879e..701da42cf120b 100644 --- a/x-pack/plugins/security/public/authentication/index.ts +++ b/x-pack/plugins/security/public/authentication/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -export type { - AuthenticationServiceSetup, - AuthenticationServiceStart, -} from './authentication_service'; export { AuthenticationService } from './authentication_service'; diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts index ccd8377cbdd43..b0f3f1059dfb4 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts @@ -7,8 +7,7 @@ import type { ApplicationSetup, AppMountParameters, StartServicesAccessor } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; - -import type { AuthenticationServiceSetup } from '../authentication_service'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; interface CreateDeps { application: ApplicationSetup; diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx index 4720a829674ee..6f39a2608e1cd 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx @@ -12,9 +12,9 @@ import ReactDOM from 'react-dom'; import type { AppMountParameters, CoreStart, IBasePath } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { parseNext } from '../../../common/parse_next'; -import type { AuthenticationServiceSetup } from '../authentication_service'; import { AuthenticationStatePage } from '../components'; interface Props { diff --git a/x-pack/plugins/security/public/components/use_current_user.ts b/x-pack/plugins/security/public/components/use_current_user.ts index 924853fa8d86b..fc86aa459f2f4 100644 --- a/x-pack/plugins/security/public/components/use_current_user.ts +++ b/x-pack/plugins/security/public/components/use_current_user.ts @@ -9,9 +9,10 @@ import constate from 'constate'; import useAsync from 'react-use/lib/useAsync'; import useObservable from 'react-use/lib/useObservable'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; + import { useSecurityApiClients } from '.'; import type { UserProfileData } from '../../common'; -import type { AuthenticationServiceSetup } from '../authentication'; export interface AuthenticationProviderProps { authc: AuthenticationServiceSetup; diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index 209bc5ff576b6..419042c4a288c 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -6,29 +6,31 @@ */ import type { PluginInitializer, PluginInitializerContext } from '@kbn/core/public'; +import type { SecurityPluginSetup } from '@kbn/security-plugin-types-public'; import type { PluginSetupDependencies, PluginStartDependencies, - SecurityPluginSetup, SecurityPluginStart, } from './plugin'; import { SecurityPlugin } from './plugin'; -export type { SecurityPluginSetup, SecurityPluginStart }; -export type { AuthenticatedUser } from '../common/model'; -export type { SecurityLicense, SecurityLicenseFeatures } from '../common/licensing'; +export type { SecurityPluginStart, SecurityPluginSetup }; +export type { AuthenticatedUser, SecurityLicenseFeatures, SecurityLicense } from '../common'; export type { UiApi, ChangePasswordProps, PersonalInfoProps } from './ui_api'; -export type { UserMenuLink, SecurityNavControlServiceStart } from './nav_control'; + +export { ALL_SPACES_ID } from '../common/constants'; + +// Re-export types from the plugin directly to enhance the developer experience for consumers of the Security plugin. export type { + AuthenticationServiceStart, + AuthenticationServiceSetup, + SecurityNavControlServiceStart, + UserMenuLink, UserProfileBulkGetParams, UserProfileGetCurrentParams, UserProfileSuggestParams, -} from './account_management'; - -export type { AuthenticationServiceStart, AuthenticationServiceSetup } from './authentication'; - -export { ALL_SPACES_ID } from '../common/constants'; +} from '@kbn/security-plugin-types-public'; export const plugin: PluginInitializer< SecurityPluginSetup, diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts index be236b02e4c65..b3d1a3a3e9cbe 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts @@ -6,11 +6,10 @@ */ import type { HttpStart } from '@kbn/core/public'; +import type { CreateAPIKeyParams, CreateAPIKeyResult } from '@kbn/security-plugin-types-server'; import type { ApiKeyToInvalidate } from '../../../common/model'; import type { - CreateAPIKeyParams, - CreateAPIKeyResult, GetAPIKeysResult, UpdateAPIKeyParams, UpdateAPIKeyResult, diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx index b1872a459d8b0..dd47e7e198bc4 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx @@ -38,7 +38,7 @@ import { UserAvatar, UserProfilesPopover } from '@kbn/user-profile-components'; import { ApiKeyFlyout } from './api_key_flyout'; import { ApiKeysEmptyPrompt } from './api_keys_empty_prompt'; import { InvalidateProvider } from './invalidate_provider'; -import type { ApiKey, AuthenticatedUser, RestApiKey } from '../../../../common/model'; +import type { ApiKey, AuthenticatedUser, RestApiKey } from '../../../../common'; import { Breadcrumb } from '../../../components/breadcrumb'; import { SelectableTokenField } from '../../../components/token_field'; import { useCapabilities } from '../../../components/use_capabilities'; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx index ac7b067e3371c..c15ea9d9e731e 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx @@ -16,9 +16,9 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { RegisterManagementAppArgs } from '@kbn/management-plugin/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { Router } from '@kbn/shared-ux-router'; -import type { AuthenticationServiceSetup } from '../../authentication'; import type { BreadcrumbsChangeHandler } from '../../components/breadcrumb'; import { Breadcrumb, diff --git a/x-pack/plugins/security/public/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts index 8591d4d6fd665..d1a21795ffbbd 100644 --- a/x-pack/plugins/security/public/management/management_service.test.ts +++ b/x-pack/plugins/security/public/management/management_service.test.ts @@ -21,8 +21,8 @@ import { ManagementService } from './management_service'; import { roleMappingsManagementApp } from './role_mappings'; import { rolesManagementApp } from './roles'; import { usersManagementApp } from './users'; +import type { SecurityLicenseFeatures } from '../../common'; import { licenseMock } from '../../common/licensing/index.mock'; -import type { SecurityLicenseFeatures } from '../../common/licensing/license_features'; import { securityMock } from '../mocks'; const mockSection = createManagementSectionMock(); diff --git a/x-pack/plugins/security/public/management/management_service.ts b/x-pack/plugins/security/public/management/management_service.ts index 616cccd7f5c99..e8fda628c22fc 100644 --- a/x-pack/plugins/security/public/management/management_service.ts +++ b/x-pack/plugins/security/public/management/management_service.ts @@ -13,13 +13,13 @@ import type { ManagementSection, ManagementSetup, } from '@kbn/management-plugin/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { apiKeysManagementApp } from './api_keys'; import { roleMappingsManagementApp } from './role_mappings'; import { rolesManagementApp } from './roles'; import { usersManagementApp } from './users'; -import type { SecurityLicense } from '../../common/licensing'; -import type { AuthenticationServiceSetup } from '../authentication'; +import type { SecurityLicense } from '../../common'; import type { PluginStartDependencies } from '../plugin'; export interface ManagementAppConfigType { diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx index 3eabf885d877d..5e329b32c353d 100644 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx +++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { Role } from '../../../common/model'; +import type { Role } from '../../../common'; import { isRoleAdmin, isRoleDeprecated, isRoleReserved, isRoleSystem } from '../../../common/model'; interface Props diff --git a/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx index ce1b6b0d0efc1..535ca02139c3f 100644 --- a/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.test.tsx @@ -13,7 +13,7 @@ import { coreMock } from '@kbn/core/public/mocks'; import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { DeleteProvider } from './delete_provider'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; import { roleMappingsAPIClientMock } from '../../index.mock'; describe('DeleteProvider', () => { diff --git a/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx index c957ad82e63dc..1eacafaa54f04 100644 --- a/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/components/delete_provider/delete_provider.tsx @@ -13,7 +13,7 @@ import type { NotificationsStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; import type { RoleMappingsAPIClient } from '../../role_mappings_api_client'; interface Props { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx index 017e5ec37a332..8ed7cee87e8c8 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx @@ -20,7 +20,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { EditRoleMappingPage } from './edit_role_mapping_page'; import { JSONRuleEditor } from './rule_editor_panel/json_rule_editor'; import { VisualRuleEditor } from './rule_editor_panel/visual_rule_editor'; -import type { Role } from '../../../../common/model'; +import type { Role } from '../../../../common'; import { RoleComboBox } from '../../role_combo_box'; import type { RolesAPIClient } from '../../roles'; import { rolesAPIClientMock } from '../../roles/roles_api_client.mock'; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx index 0d4c797a9452e..c3dc778643dea 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx @@ -26,7 +26,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { MappingInfoPanel } from './mapping_info_panel'; import { RuleEditorPanel } from './rule_editor_panel'; import { validateRoleMappingForSave } from './services/role_mapping_validation'; -import type { RoleMapping } from '../../../../common/model'; +import type { RoleMapping } from '../../../../common'; import type { RolesAPIClient } from '../../roles'; import { DeleteProvider, diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx index 8faf0fa99998a..b494b228d8a7c 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.test.tsx @@ -12,7 +12,7 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { MappingInfoPanel } from './mapping_info_panel'; -import type { Role, RoleMapping } from '../../../../../common/model'; +import type { Role, RoleMapping } from '../../../../../common'; import type { RolesAPIClient } from '../../../roles'; import { rolesAPIClientMock } from '../../../roles/roles_api_client.mock'; import { RoleSelector } from '../role_selector'; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx index 07c6b36d1949b..9634b8415ae36 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/mapping_info_panel/mapping_info_panel.tsx @@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; import type { RolesAPIClient } from '../../../roles'; import { RoleSelector } from '../role_selector'; import { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.test.tsx index 219e01512ed2f..df0374b04f5a2 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.test.tsx @@ -14,7 +14,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { AddRoleTemplateButton } from './add_role_template_button'; import { RoleSelector } from './role_selector'; import { RoleTemplateEditor } from './role_template_editor'; -import type { Role, RoleMapping } from '../../../../../common/model'; +import type { Role, RoleMapping } from '../../../../../common'; import type { RolesAPIClient } from '../../../roles'; import { rolesAPIClientMock } from '../../../roles/roles_api_client.mock'; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx index bf8b68ab7a927..8bc7aa852e2e5 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_selector.tsx @@ -14,7 +14,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { AddRoleTemplateButton } from './add_role_template_button'; import { RoleTemplateEditor } from './role_template_editor'; -import type { Role, RoleMapping } from '../../../../../common/model'; +import type { Role, RoleMapping } from '../../../../../common'; import { isRoleDeprecated } from '../../../../../common/model'; import { RoleComboBox } from '../../../role_combo_box'; import type { RolesAPIClient } from '../../../roles'; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx index 4641e0fe13bd2..68d2e81a74a60 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_editor.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { RoleTemplateTypeSelect } from './role_template_type_select'; -import type { RoleTemplate } from '../../../../../common/model'; +import type { RoleTemplate } from '../../../../../common'; import { isInlineRoleTemplate, isInvalidRoleTemplate, diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx index ae11d2bb889bb..964482fa8ee77 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/role_selector/role_template_type_select.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import type { RoleTemplate } from '../../../../../common/model'; +import type { RoleTemplate } from '../../../../../common'; import { isInlineRoleTemplate, isStoredRoleTemplate } from '../services/role_template_type'; const templateTypeOptions = [ diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx index 1a5549ecd1e9b..16111e8d535a9 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/rule_editor_panel/rule_editor_panel.tsx @@ -27,7 +27,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { JSONRuleEditor } from './json_rule_editor'; import { VisualRuleEditor } from './visual_rule_editor'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; import type { Rule } from '../../model'; import { generateRulesFromRaw } from '../../model'; import { VISUAL_MAX_RULE_DEPTH } from '../services/role_mapping_constants'; diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts index 7dfa891e8d1b0..e1a5ae41a11d5 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.test.ts @@ -12,7 +12,7 @@ import { validateRoleMappingRoleTemplates, validateRoleMappingRules, } from './role_mapping_validation'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; describe('validateRoleMappingName', () => { it('requires a value', () => { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts index 7340277158c84..4c07de0f07821 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_mapping_validation.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; -import type { RoleMapping } from '../../../../../common/model'; +import type { RoleMapping } from '../../../../../common'; import { generateRulesFromRaw } from '../../model'; interface ValidationResult { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts index 50c821541d07a..59db5c4fb9d92 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.test.ts @@ -10,7 +10,7 @@ import { isInvalidRoleTemplate, isStoredRoleTemplate, } from './role_template_type'; -import type { RoleTemplate } from '../../../../../common/model'; +import type { RoleTemplate } from '../../../../../common'; describe('#isStoredRoleTemplate', () => { it('returns true for stored templates, false otherwise', () => { diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts index ce16c2516f176..97f464ce0494f 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/services/role_template_type.ts @@ -10,7 +10,7 @@ import type { InvalidRoleTemplate, RoleTemplate, StoredRoleTemplate, -} from '../../../../../common/model'; +} from '../../../../../common'; export function isStoredRoleTemplate( roleMappingTemplate: RoleTemplate diff --git a/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts index 2db57590042df..f583310156c46 100644 --- a/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts +++ b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.test.ts @@ -8,7 +8,7 @@ import { FieldRule } from './field_rule'; import { generateRulesFromRaw } from './rule_builder'; import { RuleBuilderError } from './rule_builder_error'; -import type { RoleMapping } from '../../../../common/model'; +import type { RoleMapping } from '../../../../common'; describe('generateRulesFromRaw', () => { it('returns null for an empty rule set', () => { diff --git a/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts index b248f63410a2b..88082245decca 100644 --- a/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts +++ b/x-pack/plugins/security/public/management/role_mappings/model/rule_builder.ts @@ -15,7 +15,7 @@ import type { FieldRuleValue } from './field_rule'; import { FieldRule } from './field_rule'; import type { Rule } from './rule'; import { RuleBuilderError } from './rule_builder_error'; -import type { RoleMapping } from '../../../../common/model'; +import type { RoleMapping } from '../../../../common'; interface RuleBuilderResult { /** The maximum rule depth within the parsed rule set. */ diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts index 5465bc24b7e31..bab0222222dec 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts @@ -7,7 +7,7 @@ import type { HttpStart } from '@kbn/core/public'; -import type { RoleMapping } from '../../../common/model'; +import type { RoleMapping } from '../../../common'; export interface CheckRoleMappingFeaturesResponse { canManageRoleMappings: boolean; diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx index 0224512cdc211..b5905ec147960 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx @@ -32,7 +32,7 @@ import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { EmptyPrompt } from './empty_prompt'; -import type { Role, RoleMapping } from '../../../../common/model'; +import type { Role, RoleMapping } from '../../../../common'; import { DisabledBadge, EnabledBadge } from '../../badges'; import { EDIT_ROLE_MAPPING_PATH, diff --git a/x-pack/plugins/security/public/management/role_table_display/role_table_display.tsx b/x-pack/plugins/security/public/management/role_table_display/role_table_display.tsx index 558461a9b70d4..d52f46d467c7f 100644 --- a/x-pack/plugins/security/public/management/role_table_display/role_table_display.tsx +++ b/x-pack/plugins/security/public/management/role_table_display/role_table_display.tsx @@ -10,7 +10,7 @@ import React from 'react'; import type { ApplicationStart } from '@kbn/core/public'; -import type { Role } from '../../../common/model'; +import type { Role } from '../../../common'; import { getExtendedRoleDeprecationNotice, isRoleDeprecated } from '../../../common/model'; interface Props { diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts index 1b68f556d5990..559d479182c89 100644 --- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts +++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts @@ -9,7 +9,7 @@ import type { KibanaFeature } from '@kbn/features-plugin/public'; import { featuresPluginMock } from '@kbn/features-plugin/server/mocks'; import type { LicenseType } from '@kbn/licensing-plugin/server'; -import type { SecurityLicenseFeatures } from '../../../../common/licensing'; +import type { SecurityLicenseFeatures } from '../../../../common'; import { Actions } from '../../../../server/authorization'; import { privilegesFactory } from '../../../../server/authorization/privileges'; import { KibanaPrivileges } from '../model'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 2250846ac4a13..e3a1151c2bba8 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -24,8 +24,8 @@ import { EditRolePage } from './edit_role_page'; import { SimplePrivilegeSection } from './privileges/kibana/simple_privilege_section'; import { SpaceAwarePrivilegeSection } from './privileges/kibana/space_aware_privilege_section'; import { TransformErrorSection } from './privileges/kibana/transform_error_section'; +import type { Role } from '../../../../common'; import { licenseMock } from '../../../../common/licensing/index.mock'; -import type { Role } from '../../../../common/model'; import { userAPIClientMock } from '../../users/index.mock'; import { createRawKibanaPrivileges } from '../__fixtures__/kibana_privileges'; import { indicesAPIClientMock, privilegesAPIClientMock, rolesAPIClientMock } from '../index.mock'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 8746be6ec9087..37b6183520432 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -48,13 +48,13 @@ import { ElasticsearchPrivileges, KibanaPrivilegesRegion } from './privileges'; import { ReservedRoleBadge } from './reserved_role_badge'; import type { RoleValidationResult } from './validate_role'; import { RoleValidator } from './validate_role'; -import type { SecurityLicense } from '../../../../common/licensing'; import type { BuiltinESPrivileges, RawKibanaPrivileges, Role, RoleIndexPrivilege, -} from '../../../../common/model'; + SecurityLicense, +} from '../../../../common'; import { isRoleDeprecated as checkIfRoleDeprecated, isRoleReadOnly as checkIfRoleReadOnly, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts b/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts index 170aa3f6e89fe..da912650fee48 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privilege_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RoleKibanaPrivilege } from '../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../common'; /** * Determines if the passed privilege spec defines global privileges. diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.test.tsx index 23dcb3673192f..81edde34b4d28 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ClusterPrivileges } from './cluster_privileges'; -import type { Role } from '../../../../../../common/model'; +import type { Role } from '../../../../../../common'; test('it renders without crashing', () => { const role: Role = { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx index c5463c4458b11..8e8f4aa0a2cbc 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/cluster_privileges.tsx @@ -11,7 +11,7 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; -import type { Role } from '../../../../../../common/model'; +import type { Role } from '../../../../../../common'; import { isRoleReadOnly } from '../../../../../../common/model'; interface Props { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx index c389788d7994b..99ce696212888 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/elasticsearch_privileges.tsx @@ -24,8 +24,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { ClusterPrivileges } from './cluster_privileges'; import { IndexPrivileges } from './index_privileges'; -import type { SecurityLicense } from '../../../../../../common/licensing'; -import type { BuiltinESPrivileges, Role } from '../../../../../../common/model'; +import type { BuiltinESPrivileges, Role, SecurityLicense } from '../../../../../../common'; import type { IndicesAPIClient } from '../../../indices_api_client'; import { CollapsiblePanel } from '../../collapsible_panel'; import type { RoleValidator } from '../../validate_role'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx index b43064a7d1150..dcfd57f56464c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privilege_form.tsx @@ -27,7 +27,7 @@ import type { monaco } from '@kbn/monaco'; import type { Cluster } from '@kbn/remote-clusters-plugin/public'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../../../common/model'; +import type { RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../../../common'; import type { IndicesAPIClient } from '../../../indices_api_client'; import type { RoleValidator } from '../../validate_role'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx index dafb7bae8ee9e..435e094999c24 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/index_privileges.tsx @@ -13,8 +13,7 @@ import type { Cluster } from '@kbn/remote-clusters-plugin/public'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { IndexPrivilegeForm } from './index_privilege_form'; -import type { SecurityLicense } from '../../../../../../common/licensing'; -import type { Role, RoleIndexPrivilege } from '../../../../../../common/model'; +import type { Role, RoleIndexPrivilege, SecurityLicense } from '../../../../../../common'; import { isRoleEnabled, isRoleReadOnly } from '../../../../../../common/model'; import type { IndicesAPIClient } from '../../../indices_api_client'; import type { RoleValidator } from '../../validate_role'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx index 59418010b114d..c487cdab96e2d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx @@ -13,7 +13,7 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { getDisplayedFeaturePrivileges } from './__fixtures__'; import { FeatureTable } from './feature_table'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { createFeature, kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx index 8d9573383b255..7734d415bf385 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx @@ -32,7 +32,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ChangeAllPrivilegesControl } from './change_all_privileges'; import { FeatureTableExpandedRow } from './feature_table_expanded_row'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import type { KibanaPrivileges, SecuredFeature } from '../../../../model'; import { NO_PRIVILEGE_VALUE } from '../constants'; import { FeatureTableCell } from '../feature_table_cell'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table_expanded_row.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table_expanded_row.test.tsx index 42a08b3244363..80da1f4092144 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table_expanded_row.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table_expanded_row.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { FeatureTableExpandedRow } from './feature_table_expanded_row'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/sub_feature_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/sub_feature_form.test.tsx index 3047cb0f91f12..53e44aefbf1c8 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/sub_feature_form.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/sub_feature_form.test.tsx @@ -13,7 +13,7 @@ import { KibanaFeature } from '@kbn/features-plugin/public'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { SubFeatureForm } from './sub_feature_form'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { SecuredSubFeature } from '../../../../model'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx index ef6e4ca485d0b..b12c4f91a3a7a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.test.tsx @@ -16,7 +16,7 @@ import { KibanaPrivilegesRegion } from './kibana_privileges_region'; import { SimplePrivilegeSection } from './simple_privilege_section'; import { SpaceAwarePrivilegeSection } from './space_aware_privilege_section'; import { TransformErrorSection } from './transform_error_section'; -import type { Role } from '../../../../../../common/model'; +import type { Role } from '../../../../../../common'; import { KibanaPrivileges } from '../../../model'; import { RoleValidator } from '../../validate_role'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx index e45829d722cbc..d7439b19b0d00 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx @@ -13,7 +13,7 @@ import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public'; import { SimplePrivilegeSection } from './simple_privilege_section'; import { SpaceAwarePrivilegeSection } from './space_aware_privilege_section'; import { TransformErrorSection } from './transform_error_section'; -import type { Role } from '../../../../../../common/model'; +import type { Role } from '../../../../../../common'; import type { KibanaPrivileges } from '../../../model'; import { CollapsiblePanel } from '../../collapsible_panel'; import type { RoleValidator } from '../../validate_role'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.test.ts index 901cd14e24038..dc2f03731c650 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.test.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.test.ts @@ -6,7 +6,7 @@ */ import { PrivilegeFormCalculator } from './privilege_form_calculator'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.ts index 796cfea92b43e..d6afdaf6efafd 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_form_calculator/privilege_form_calculator.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import type { KibanaPrivileges, SubFeaturePrivilegeGroup } from '../../../../model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/__fixtures__/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/__fixtures__/index.ts index f375263c960c3..6d9cb86ace188 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/__fixtures__/index.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/__fixtures__/index.ts @@ -10,7 +10,7 @@ import type { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@kbn/test-jest-helpers'; -import type { Role, RoleKibanaPrivilege } from '../../../../../../../../common/model'; +import type { Role, RoleKibanaPrivilege } from '../../../../../../../../common'; import { FeatureTableCell } from '../../feature_table_cell'; import { PrivilegeSummaryExpandedRow } from '../privilege_summary_expanded_row'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx index 7de3c66f8f4f5..9f6aa8ed69ed9 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx @@ -15,7 +15,7 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { PrivilegeSummary } from './privilege_summary'; import { PrivilegeSummaryTable } from './privilege_summary_table'; -import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx index d5a98510b0265..5c6d03569b10a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx @@ -20,7 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public'; import { PrivilegeSummaryTable } from './privilege_summary_table'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import type { KibanaPrivileges } from '../../../../model'; interface Props { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.test.ts index 856404408d55c..f2869c11f13c6 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.test.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.test.ts @@ -6,7 +6,7 @@ */ import { PrivilegeSummaryCalculator } from './privilege_summary_calculator'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.ts index 14e2241cdf830..053cd19c98d58 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_calculator.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { Role, RoleKibanaPrivilege } from '../../../../../../../common'; import type { KibanaPrivileges, PrimaryFeaturePrivilege, SecuredFeature } from '../../../../model'; import type { PrivilegeCollection } from '../../../../model/privilege_collection'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx index 7efe5bc8333fd..22ec00d393c9c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx @@ -16,7 +16,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { getDisplayedFeaturePrivileges } from './__fixtures__'; import type { PrivilegeSummaryTableProps } from './privilege_summary_table'; import { PrivilegeSummaryTable } from './privilege_summary_table'; -import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../../../../common'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx index 4bddc6bad6fda..7dcbbe85d553c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx @@ -26,8 +26,8 @@ import type { EffectiveFeaturePrivileges } from './privilege_summary_calculator' import { PrivilegeSummaryCalculator } from './privilege_summary_calculator'; import { PrivilegeSummaryExpandedRow } from './privilege_summary_expanded_row'; import { SpaceColumnHeader } from './space_column_header'; +import type { Role, RoleKibanaPrivilege } from '../../../../../../../common'; import { ALL_SPACES_ID } from '../../../../../../../common/constants'; -import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; import type { KibanaPrivileges, PrimaryFeaturePrivilege, SecuredFeature } from '../../../../model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { FeatureTableCell } from '../feature_table_cell'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx index 61a7c024a2828..4c1ebbbfffd3a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx @@ -15,7 +15,7 @@ import { getUiApi } from '@kbn/spaces-plugin/public/ui_api'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { SpaceColumnHeader } from './space_column_header'; -import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../../../../common'; import { SpacesPopoverList } from '../../../spaces_popover_list'; const spaces = [ diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx index 99a4cb0020aed..ca4a2d6011c58 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public'; -import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../../../../common'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { SpacesPopoverList } from '../../../spaces_popover_list'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx index 85f8af876dae7..e336d75969246 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.test.tsx @@ -13,7 +13,7 @@ import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers'; import { SimplePrivilegeSection } from './simple_privilege_section'; import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { KibanaPrivileges, SecuredFeature } from '../../../../model'; const buildProps = (customProps: any = {}) => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx index 786039ce0a237..2e8b395ea07a7 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/simple_privilege_section/simple_privilege_section.tsx @@ -18,7 +18,7 @@ import React, { Component, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { UnsupportedSpacePrivilegesWarning } from './unsupported_space_privileges_warning'; -import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { Role, RoleKibanaPrivilege } from '../../../../../../../common'; import { copyRole } from '../../../../../../../common/model'; import type { KibanaPrivileges } from '../../../../model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx index 4fdeff85fb00d..d50ab47160923 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx @@ -13,7 +13,7 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { PrivilegeSpaceForm } from './privilege_space_form'; import { SpaceSelector } from './space_selector'; -import type { Role } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; import { createFeature, kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { FeatureTable } from '../feature_table'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx index 05327142e2105..c3c285a57418d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx @@ -32,8 +32,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { Space } from '@kbn/spaces-plugin/public'; import { SpaceSelector } from './space_selector'; +import type { FeaturesPrivileges, Role } from '../../../../../../../common'; import { ALL_SPACES_ID } from '../../../../../../../common/constants'; -import type { FeaturesPrivileges, Role } from '../../../../../../../common/model'; import { copyRole } from '../../../../../../../common/model'; import type { KibanaPrivileges } from '../../../../model'; import { CUSTOM_PRIVILEGE_VALUE } from '../constants'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx index 5c9220872d9b3..6de312d036981 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx @@ -14,7 +14,7 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { PrivilegeDisplay } from './privilege_display'; import { PrivilegeSpaceTable } from './privilege_space_table'; -import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; +import type { Role, RoleKibanaPrivilege } from '../../../../../../../common'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx index adfc8100aeb93..cbbbc96863bda 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx @@ -26,7 +26,7 @@ import type { Space } from '@kbn/spaces-plugin/public'; import { getSpaceColor } from '@kbn/spaces-plugin/public'; import { PrivilegeDisplay } from './privilege_display'; -import type { FeaturesPrivileges, Role } from '../../../../../../../common/model'; +import type { FeaturesPrivileges, Role } from '../../../../../../../common'; import { copyRole } from '../../../../../../../common/model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { CUSTOM_PRIVILEGE_VALUE } from '../constants'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index 2031569169571..1de6a8a952a50 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -24,8 +24,8 @@ import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public'; import { PrivilegeSpaceForm } from './privilege_space_form'; import { PrivilegeSpaceTable } from './privilege_space_table'; -import type { Role } from '../../../../../../../common/model'; -import { isRoleReserved } from '../../../../../../../common/model'; +import type { Role } from '../../../../../../../common'; +import { isRoleReserved } from '../../../../../../../common'; import type { KibanaPrivileges } from '../../../../model'; import type { RoleValidator } from '../../../validate_role'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx index ac3c36c510bcd..2b6efe0f0ab5a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.test.tsx @@ -10,7 +10,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import { ReservedRoleBadge } from './reserved_role_badge'; -import type { Role } from '../../../../common/model'; +import type { Role } from '../../../../common'; const reservedRole: Role = { name: '', diff --git a/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx index 2eec303c8dc53..8f79bd2a5788e 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/reserved_role_badge.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { Role } from '../../../../common/model'; -import { isRoleReserved } from '../../../../common/model'; +import type { Role } from '../../../../common'; +import { isRoleReserved } from '../../../../common'; interface Props { role: Role; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts index f2f2f9a10e52c..f1acb67fef92f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.test.ts @@ -6,7 +6,7 @@ */ import { RoleValidator } from './validate_role'; -import type { Role } from '../../../../common/model'; +import type { Role } from '../../../../common'; let validator: RoleValidator; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts index ee3e85959e312..18728994a5e4e 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; +import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../common'; import { MAX_NAME_LENGTH, NAME_REGEX } from '../../../../common/constants'; -import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../../common/model'; interface RoleValidatorOptions { shouldValidate?: boolean; diff --git a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.test.ts b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.test.ts index 5baaf7d08055b..494f5a14b1e48 100644 --- a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.test.ts +++ b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.test.ts @@ -7,7 +7,7 @@ import { KibanaPrivilege } from './kibana_privilege'; import { KibanaPrivileges } from './kibana_privileges'; -import type { RoleKibanaPrivilege } from '../../../../common/model'; +import type { RoleKibanaPrivilege } from '../../../../common'; import { kibanaFeatures } from '../__fixtures__/kibana_features'; import { createRawKibanaPrivileges } from '../__fixtures__/kibana_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts index 7e5151d6d67af..78b312c123a3f 100644 --- a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts +++ b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts @@ -10,7 +10,7 @@ import type { KibanaFeature } from '@kbn/features-plugin/common'; import { KibanaPrivilege } from './kibana_privilege'; import { PrivilegeCollection } from './privilege_collection'; import { SecuredFeature } from './secured_feature'; -import type { RawKibanaPrivileges, RoleKibanaPrivilege } from '../../../../common/model'; +import type { RawKibanaPrivileges, RoleKibanaPrivilege } from '../../../../common'; import { isGlobalPrivilegeDefinition } from '../edit_role/privilege_utils'; function toBasePrivilege(entry: [string, string[]]): [string, KibanaPrivilege] { diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts index ac30132df8a80..e7f4839e56c5d 100644 --- a/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts +++ b/x-pack/plugins/security/public/management/roles/roles_api_client.test.ts @@ -8,7 +8,7 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; import { RolesAPIClient } from './roles_api_client'; -import type { Role } from '../../../common/model'; +import type { Role } from '../../../common'; describe('RolesAPIClient', () => { async function saveRole(role: Role) { diff --git a/x-pack/plugins/security/public/management/roles/roles_api_client.ts b/x-pack/plugins/security/public/management/roles/roles_api_client.ts index 6d59976a82ada..3742569e9cc72 100644 --- a/x-pack/plugins/security/public/management/roles/roles_api_client.ts +++ b/x-pack/plugins/security/public/management/roles/roles_api_client.ts @@ -7,7 +7,7 @@ import type { HttpStart } from '@kbn/core/public'; -import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../common/model'; +import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../common'; import { copyRole } from '../../../common/model'; export class RolesAPIClient { diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx index 4c6962585976c..6a6433038d635 100644 --- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx @@ -30,7 +30,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { ConfirmDelete } from './confirm_delete'; import { PermissionDenied } from './permission_denied'; -import type { Role } from '../../../../common/model'; +import type { Role } from '../../../../common'; import { getExtendedRoleDeprecationNotice, isRoleDeprecated, diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index 52e86272fd504..a77586cdad599 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -15,7 +15,7 @@ import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-pl import type { RegisterManagementAppArgs } from '@kbn/management-plugin/public'; import { Route, Router } from '@kbn/shared-ux-router'; -import type { SecurityLicense } from '../../../common/licensing'; +import type { SecurityLicense } from '../../../common'; import { Breadcrumb, BreadcrumbsProvider, diff --git a/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx index 4ab78b64bf9ae..07a80d5c4633a 100644 --- a/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx +++ b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.test.tsx @@ -13,7 +13,7 @@ import { coreMock } from '@kbn/core/public/mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ChangePasswordForm } from './change_password_form'; -import type { User } from '../../../../../common/model'; +import type { User } from '../../../../../common'; import { userAPIClientMock } from '../../index.mock'; function getCurrentPasswordField(wrapper: ReactWrapper) { diff --git a/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx index d30eabc7f35e4..c1bd329d349cf 100644 --- a/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx +++ b/x-pack/plugins/security/public/management/users/components/change_password_form/change_password_form.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { User } from '../../../../../common/model'; +import type { User } from '../../../../../common'; import type { UserAPIClient } from '../../user_api_client'; interface Props { diff --git a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx index 41c29ab773868..226ed7bc3345d 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/user_form.tsx @@ -27,8 +27,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { Role, User } from '../../../../common'; import { MAX_NAME_LENGTH, NAME_REGEX } from '../../../../common/constants'; -import type { Role, User } from '../../../../common/model'; import { isRoleDeprecated } from '../../../../common/model'; import { DocLink } from '../../../components/doc_link'; import type { ValidationErrors } from '../../../components/use_form'; diff --git a/x-pack/plugins/security/public/management/users/user_api_client.ts b/x-pack/plugins/security/public/management/users/user_api_client.ts index 071aa9e95171b..1b3e8a9866717 100644 --- a/x-pack/plugins/security/public/management/users/user_api_client.ts +++ b/x-pack/plugins/security/public/management/users/user_api_client.ts @@ -7,7 +7,7 @@ import type { HttpStart } from '@kbn/core/public'; -import type { EditUser, User } from '../../../common/model'; +import type { EditUser, User } from '../../../common'; const usersUrl = '/internal/security/users'; diff --git a/x-pack/plugins/security/public/management/users/user_utils.test.ts b/x-pack/plugins/security/public/management/users/user_utils.test.ts index a2d3fddf7725d..00164b323ba78 100644 --- a/x-pack/plugins/security/public/management/users/user_utils.test.ts +++ b/x-pack/plugins/security/public/management/users/user_utils.test.ts @@ -6,7 +6,7 @@ */ import { getExtendedUserDeprecationNotice, isUserDeprecated, isUserReserved } from './user_utils'; -import type { User } from '../../../common/model'; +import type { User } from '../../../common'; describe('#isUserReserved', () => { it('returns false for a user with no metadata', () => { diff --git a/x-pack/plugins/security/public/management/users/user_utils.ts b/x-pack/plugins/security/public/management/users/user_utils.ts index bc7da560efcce..91a8f58439f67 100644 --- a/x-pack/plugins/security/public/management/users/user_utils.ts +++ b/x-pack/plugins/security/public/management/users/user_utils.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; -import type { User } from '../../../common/model'; +import type { User } from '../../../common'; export const isUserReserved = (user: User) => user.metadata?._reserved ?? false; diff --git a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx index dd7b68566a197..c384b133db61f 100644 --- a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.test.tsx @@ -15,7 +15,7 @@ import { coreMock, scopedHistoryMock } from '@kbn/core/public/mocks'; import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { UsersGridPage } from './users_grid_page'; -import type { User } from '../../../../common/model'; +import type { User } from '../../../../common'; import { rolesAPIClientMock } from '../../roles/index.mock'; import { userAPIClientMock } from '../index.mock'; diff --git a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx index 35d54d437d761..071de42046706 100644 --- a/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx +++ b/x-pack/plugins/security/public/management/users/users_grid/users_grid_page.tsx @@ -26,7 +26,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { Role, User } from '../../../../common/model'; +import type { Role, User } from '../../../../common'; import { DeprecatedBadge, DisabledBadge, ReservedBadge } from '../../badges'; import { RoleTableDisplay } from '../../role_table_display'; import type { RolesAPIClient } from '../../roles'; diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx index c44ff5e5ceb93..ffb7d1e5618d9 100644 --- a/x-pack/plugins/security/public/management/users/users_management_app.tsx +++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx @@ -17,9 +17,9 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { RegisterManagementAppArgs } from '@kbn/management-plugin/public'; +import type { AuthenticationServiceSetup } from '@kbn/security-plugin-types-public'; import { Route, Router, Routes } from '@kbn/shared-ux-router'; -import type { AuthenticationServiceSetup } from '../../authentication'; import type { BreadcrumbsChangeHandler } from '../../components/breadcrumb'; import { Breadcrumb, diff --git a/x-pack/plugins/security/public/nav_control/index.mock.ts b/x-pack/plugins/security/public/nav_control/index.mock.ts index 769007d0bc0cd..2ca7212c49b86 100644 --- a/x-pack/plugins/security/public/nav_control/index.mock.ts +++ b/x-pack/plugins/security/public/nav_control/index.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SecurityNavControlServiceStart } from './nav_control_service'; +import type { SecurityNavControlServiceStart } from '@kbn/security-plugin-types-public'; export const navControlServiceMock = { createStart: (): jest.Mocked => ({ diff --git a/x-pack/plugins/security/public/nav_control/index.ts b/x-pack/plugins/security/public/nav_control/index.ts index 95331b7504070..c008a8f15d1f1 100644 --- a/x-pack/plugins/security/public/nav_control/index.ts +++ b/x-pack/plugins/security/public/nav_control/index.ts @@ -5,6 +5,4 @@ * 2.0. */ -export type { SecurityNavControlServiceStart } from './nav_control_service'; export { SecurityNavControlService } from './nav_control_service'; -export type { UserMenuLink } from './nav_control_component'; diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx index b2f05f9c6d568..c043f837845db 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import type { EuiContextMenuPanelItemDescriptor, IconType } from '@elastic/eui'; +import type { EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import { EuiContextMenu, EuiContextMenuItem, @@ -23,21 +23,12 @@ import type { Observable } from 'rxjs'; import type { BuildFlavor } from '@kbn/config/src/types'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { UserMenuLink } from '@kbn/security-plugin-types-public'; import { UserAvatar, type UserProfileAvatarData } from '@kbn/user-profile-components'; import { getUserDisplayName, isUserAnonymous } from '../../common/model'; import { useCurrentUser, useUserProfile } from '../components'; -export interface UserMenuLink { - label: string; - iconType: IconType; - href: string; - order?: number; - setAsProfile?: boolean; - /** Render a custom ReactNode instead of the default */ - content?: ReactNode; -} - type ContextMenuItem = EuiContextMenuPanelItemDescriptor & { content?: ReactNode }; interface ContextMenuProps { diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index 0bcc3a58263fb..bfea42ca97e90 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -17,12 +17,15 @@ import type { BuildFlavor } from '@kbn/config/src/types'; import type { CoreStart, CoreTheme } from '@kbn/core/public'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import type { + AuthenticationServiceSetup, + SecurityNavControlServiceStart, + UserMenuLink, +} from '@kbn/security-plugin-types-public'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; -import type { UserMenuLink } from './nav_control_component'; import { SecurityNavControl } from './nav_control_component'; -import type { SecurityLicense } from '../../common/licensing'; -import type { AuthenticationServiceSetup } from '../authentication'; +import type { SecurityLicense } from '../../common'; import type { SecurityApiClients } from '../components'; import { AuthenticationProvider, SecurityApiClientsProvider } from '../components'; @@ -37,18 +40,6 @@ interface StartDeps { authc: AuthenticationServiceSetup; } -export interface SecurityNavControlServiceStart { - /** - * Returns an Observable of the array of user menu links (the links that show up under the user's Avatar in the UI) registered by other plugins - */ - getUserMenuLinks$: () => Observable; - - /** - * Registers the provided user menu links to be displayed in the user menu (the links that show up under the user's Avatar in the UI). - */ - addUserMenuLinks: (newUserMenuLink: UserMenuLink[]) => void; -} - export class SecurityNavControlService { private securityLicense!: SecurityLicense; private logoutUrl!: string; diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index eb5b2723f9eab..1927ebb21d8e1 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -19,25 +19,28 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { i18n } from '@kbn/i18n'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/public'; import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; +import type { + AuthenticationServiceSetup, + AuthenticationServiceStart, + SecurityPluginSetup, + SecurityPluginStart as SecurityPluginStartWithoutDeprecatedMembers, +} from '@kbn/security-plugin-types-public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { accountManagementApp, UserProfileAPIClient } from './account_management'; import { AnalyticsService } from './analytics'; import { AnonymousAccessService } from './anonymous_access'; -import type { AuthenticationServiceSetup, AuthenticationServiceStart } from './authentication'; import { AuthenticationService } from './authentication'; import type { SecurityApiClients } from './components'; import type { ConfigType } from './config'; import { ManagementService, UserAPIClient } from './management'; -import type { SecurityNavControlServiceStart } from './nav_control'; import { SecurityNavControlService } from './nav_control'; import { SecurityCheckupService } from './security_checkup'; import { SessionExpired, SessionTimeout, UnauthorizedResponseHttpInterceptor } from './session'; import type { UiApi } from './ui_api'; import { getUiApi } from './ui_api'; import { SecurityLicenseService } from '../common/licensing'; -import type { SecurityLicense } from '../common/licensing'; export interface PluginSetupDependencies { licensing: LicensingPluginSetup; @@ -230,34 +233,7 @@ function getLogoutUrl(http: HttpSetup) { return `${http.basePath.serverBasePath}/logout`; } -export interface SecurityPluginSetup { - /** - * Exposes authentication information about the currently logged in user. - */ - authc: AuthenticationServiceSetup; - /** - * Exposes information about the available security features under the current license. - */ - license: SecurityLicense; -} - -export interface SecurityPluginStart { - /** - * Exposes the ability to add custom links to the dropdown menu in the top right, where the user's Avatar is. - */ - navControlService: SecurityNavControlServiceStart; - /** - * Exposes authentication information about the currently logged in user. - */ - authc: AuthenticationServiceStart; - /** - * A set of methods to work with Kibana user profiles. - */ - userProfiles: Pick< - UserProfileAPIClient, - 'getCurrent' | 'bulkGet' | 'suggest' | 'update' | 'userProfile$' - >; - +export interface SecurityPluginStart extends SecurityPluginStartWithoutDeprecatedMembers { /** * Exposes UI components that will be loaded asynchronously. * @deprecated diff --git a/x-pack/plugins/security/public/ui_api/change_password/change_password.tsx b/x-pack/plugins/security/public/ui_api/change_password/change_password.tsx index 65e98a39407f0..1e5fc6ee88b29 100644 --- a/x-pack/plugins/security/public/ui_api/change_password/change_password.tsx +++ b/x-pack/plugins/security/public/ui_api/change_password/change_password.tsx @@ -12,7 +12,7 @@ import type { NotificationsStart } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { AuthenticatedUser } from '../../../common/model'; +import type { AuthenticatedUser } from '../../../common'; import { canUserChangePassword } from '../../../common/model'; import type { UserAPIClient } from '../../management/users'; import { ChangePasswordForm } from '../../management/users/components/change_password_form'; diff --git a/x-pack/plugins/security/public/ui_api/personal_info/personal_info.tsx b/x-pack/plugins/security/public/ui_api/personal_info/personal_info.tsx index e0d9bb78cf595..339c62232b495 100644 --- a/x-pack/plugins/security/public/ui_api/personal_info/personal_info.tsx +++ b/x-pack/plugins/security/public/ui_api/personal_info/personal_info.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { AuthenticatedUser } from '../../../common/model'; +import type { AuthenticatedUser } from '../../../common'; export interface PersonalInfoProps { user: AuthenticatedUser; diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index b0edfa991a13c..a5e080787e175 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -5,120 +5,17 @@ * 2.0. */ -import type { EcsEvent, KibanaRequest, LogMeta } from '@kbn/core/server'; +import type { EcsEvent, KibanaRequest } from '@kbn/core/server'; +import type { AuditEvent } from '@kbn/security-plugin-types-server'; import type { ArrayElement } from '@kbn/utility-types'; -import type { AuthenticationProvider } from '../../common/model'; +import type { AuthenticationProvider } from '../../common'; import type { AuthenticationResult } from '../authentication/authentication_result'; import type { AuditAction, AddAuditEventParams as SavedObjectEventParams, } from '../saved_objects/saved_objects_security_extension'; -/** - * Audit kibana schema using ECS format - */ -export interface AuditKibana { - /** - * The ID of the space associated with this event. - */ - space_id?: string; - /** - * The ID of the user session associated with this event. Each login attempt - * results in a unique session id. - */ - session_id?: string; - /** - * Saved object that was created, changed, deleted or accessed as part of this event. - */ - saved_object?: { - type: string; - id: string; - }; - /** - * Name of authentication provider associated with a login event. - */ - authentication_provider?: string; - /** - * Type of authentication provider associated with a login event. - */ - authentication_type?: string; - /** - * Name of Elasticsearch realm that has authenticated the user. - */ - authentication_realm?: string; - /** - * Name of Elasticsearch realm where the user details were retrieved from. - */ - lookup_realm?: string; - /** - * Set of space IDs that a saved object was shared to. - */ - add_to_spaces?: readonly string[]; - /** - * Set of space IDs that a saved object was removed from. - */ - delete_from_spaces?: readonly string[]; - /** - * Set of space IDs that are not authorized for an action. - */ - unauthorized_spaces?: readonly string[]; - /** - * Set of types that are not authorized for an action. - */ - unauthorized_types?: readonly string[]; -} - -type EcsHttp = Required['http']; -type EcsRequest = Required['request']; - -/** - * Audit request schema using ECS format - */ -export interface AuditRequest extends EcsRequest { - /** - * HTTP request headers - */ - headers?: { - 'x-forwarded-for'?: string; - }; -} - -/** - * Audit http schema using ECS format - */ -export interface AuditHttp extends EcsHttp { - /** - * HTTP request details - */ - request?: AuditRequest; -} - -/** - * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html - * - * If you add additional fields to the schema ensure you update the Kibana Filebeat module: - * https://github.com/elastic/beats/tree/master/filebeat/module/kibana - * - * @public - */ -export interface AuditEvent extends LogMeta { - /** - * Log message - */ - message: string; - - /** - * Kibana specific fields - */ - kibana?: AuditKibana; - - /** - * Fields describing an HTTP request - */ - http?: AuditHttp; -} - export interface HttpRequestParams { request: KibanaRequest; } diff --git a/x-pack/plugins/security/server/audit/audit_service.test.ts b/x-pack/plugins/security/server/audit/audit_service.test.ts index cf90c7222e5a4..773d5cdf1b8fd 100644 --- a/x-pack/plugins/security/server/audit/audit_service.test.ts +++ b/x-pack/plugins/security/server/audit/audit_service.test.ts @@ -16,8 +16,8 @@ import { httpServiceMock, loggingSystemMock, } from '@kbn/core/server/mocks'; +import type { AuditEvent } from '@kbn/security-plugin-types-server'; -import type { AuditEvent } from './audit_events'; import { AuditService, createLoggingConfig, diff --git a/x-pack/plugins/security/server/audit/audit_service.ts b/x-pack/plugins/security/server/audit/audit_service.ts index 12180808ae477..89e0efa4113fb 100644 --- a/x-pack/plugins/security/server/audit/audit_service.ts +++ b/x-pack/plugins/security/server/audit/audit_service.ts @@ -14,80 +14,17 @@ import type { LoggerContextConfigInput, LoggingServiceSetup, } from '@kbn/core/server'; +import type { AuditEvent, AuditLogger, AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; -import type { AuditEvent } from './audit_events'; import { httpRequestEvent } from './audit_events'; -import type { SecurityLicense, SecurityLicenseFeatures } from '../../common/licensing'; +import type { SecurityLicense, SecurityLicenseFeatures } from '../../common'; import type { ConfigType } from '../config'; import type { SecurityPluginSetup } from '../plugin'; export const ECS_VERSION = '1.6.0'; export const RECORD_USAGE_INTERVAL = 60 * 60 * 1000; // 1 hour -export interface AuditLogger { - /** - * Logs an {@link AuditEvent} and automatically adds meta data about the - * current user, space and correlation id. - * - * Guidelines around what events should be logged and how they should be - * structured can be found in: `/x-pack/plugins/security/README.md` - * - * @example - * ```typescript - * const auditLogger = securitySetup.audit.asScoped(request); - * auditLogger.log({ - * message: 'User is updating dashboard [id=123]', - * event: { - * action: 'saved_object_update', - * outcome: 'unknown' - * }, - * kibana: { - * saved_object: { type: 'dashboard', id: '123' } - * }, - * }); - * ``` - */ - log: (event: AuditEvent | undefined) => void; - - /** - * Indicates whether audit logging is enabled or not. - * - * Useful for skipping resource-intense operations that don't need to be performed when audit - * logging is disabled. - */ - readonly enabled: boolean; -} - -export interface AuditServiceSetup { - /** - * Creates an {@link AuditLogger} scoped to the current request. - * - * This audit logger logs events with all required user and session info and should be used for - * all user-initiated actions. - * - * @example - * ```typescript - * const auditLogger = securitySetup.audit.asScoped(request); - * auditLogger.log(event); - * ``` - */ - asScoped: (request: KibanaRequest) => AuditLogger; - - /** - * {@link AuditLogger} for background tasks only. - * - * This audit logger logs events without any user or session info and should never be used to log - * user-initiated actions. - * - * @example - * ```typescript - * securitySetup.audit.withoutRequest.log(event); - * ``` - */ - withoutRequest: AuditLogger; -} - interface AuditServiceSetupParams { license: SecurityLicense; config: ConfigType['audit']; diff --git a/x-pack/plugins/security/server/audit/index.ts b/x-pack/plugins/security/server/audit/index.ts index c21a9625ca6cf..5755f9b55e9a6 100644 --- a/x-pack/plugins/security/server/audit/index.ts +++ b/x-pack/plugins/security/server/audit/index.ts @@ -5,9 +5,7 @@ * 2.0. */ -export type { AuditServiceSetup, AuditLogger } from './audit_service'; export { AuditService } from './audit_service'; -export type { AuditEvent, AuditHttp, AuditKibana, AuditRequest } from './audit_events'; export { userLoginEvent, userLogoutEvent, diff --git a/x-pack/plugins/security/server/audit/mocks.ts b/x-pack/plugins/security/server/audit/mocks.ts index 6485818e7fc58..3544f098ece52 100644 --- a/x-pack/plugins/security/server/audit/mocks.ts +++ b/x-pack/plugins/security/server/audit/mocks.ts @@ -5,7 +5,9 @@ * 2.0. */ -import type { AuditLogger, AuditService } from './audit_service'; +import type { AuditLogger } from '@kbn/security-plugin-types-server'; + +import type { AuditService } from './audit_service'; export const auditLoggerMock = { create() { diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts index da411cf5d8c97..8f0e58acf75ad 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts @@ -16,8 +16,8 @@ import { import type { Logger } from '@kbn/logging'; import { APIKeys } from './api_keys'; +import type { SecurityLicense } from '../../../common'; import { ALL_SPACES_ID } from '../../../common/constants'; -import type { SecurityLicense } from '../../../common/licensing'; import { licenseMock } from '../../../common/licensing/index.mock'; const encodeToBase64 = (str: string) => Buffer.from(str).toString('base64'); diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts index 4f3f802d576f9..75f6d894e65eb 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts @@ -9,33 +9,28 @@ import type { IClusterClient, KibanaRequest, Logger } from '@kbn/core/server'; import type { KibanaFeature } from '@kbn/features-plugin/server'; - -import { getFakeKibanaRequest } from './fake_kibana_request'; -import type { SecurityLicense } from '../../../common/licensing'; -import { transformPrivilegesToElasticsearchPrivileges, validateKibanaPrivileges } from '../../lib'; import type { + APIKeys as APIKeysType, CreateAPIKeyParams, CreateAPIKeyResult, - CreateCrossClusterAPIKeyParams, CreateRestAPIKeyParams, CreateRestAPIKeyWithKibanaPrivilegesParams, - UpdateAPIKeyParams, - UpdateAPIKeyResult, -} from '../../routes/api_keys'; + GrantAPIKeyResult, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, +} from '@kbn/security-plugin-types-server'; + +import { getFakeKibanaRequest } from './fake_kibana_request'; +import type { SecurityLicense } from '../../../common'; +import { transformPrivilegesToElasticsearchPrivileges, validateKibanaPrivileges } from '../../lib'; +import type { UpdateAPIKeyParams, UpdateAPIKeyResult } from '../../routes/api_keys'; import { BasicHTTPAuthorizationHeaderCredentials, HTTPAuthorizationHeader, } from '../http_authentication'; -export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - UpdateAPIKeyParams, - UpdateAPIKeyResult, -}; +export type { UpdateAPIKeyParams, UpdateAPIKeyResult }; /** * Represents the options to create an APIKey class instance that will be @@ -62,76 +57,10 @@ type GrantAPIKeyParams = access_token: string; }; -/** - * Represents the params for invalidating multiple API keys - */ -export interface InvalidateAPIKeysParams { - ids: string[]; -} - -export interface GrantAPIKeyResult { - /** - * Unique id for this API key - */ - id: string; - /** - * Name for this API key - */ - name: string; - /** - * Generated API key - */ - api_key: string; -} - -/** - * The return value when invalidating an API key in Elasticsearch. - */ -export interface InvalidateAPIKeyResult { - /** - * The IDs of the API keys that were invalidated as part of the request. - */ - invalidated_api_keys: string[]; - /** - * The IDs of the API keys that were already invalidated. - */ - previously_invalidated_api_keys: string[]; - /** - * The number of errors that were encountered when invalidating the API keys. - */ - error_count: number; - /** - * Details about these errors. This field is not present in the response when error_count is 0. - */ - error_details?: Array<{ - type?: string; - reason?: string; - caused_by?: { - type?: string; - reason?: string; - }; - }>; -} - -/** - * Represents the parameters for validating API Key credentials. - */ -export interface ValidateAPIKeyParams { - /** - * Unique id for this API key - */ - id: string; - - /** - * Generated API Key (secret) - */ - api_key: string; -} - /** * Class responsible for managing Elasticsearch API keys. */ -export class APIKeys { +export class APIKeys implements APIKeysType { private readonly logger: Logger; private readonly clusterClient: IClusterClient; private readonly license: SecurityLicense; diff --git a/x-pack/plugins/security/server/authentication/api_keys/index.ts b/x-pack/plugins/security/server/authentication/api_keys/index.ts index ae9e9c98c149b..8d1aecc01216f 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/index.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/index.ts @@ -5,15 +5,4 @@ * 2.0. */ -export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - InvalidateAPIKeyResult, - InvalidateAPIKeysParams, - ValidateAPIKeyParams, - GrantAPIKeyResult, -} from './api_keys'; export { APIKeys, CreateApiKeyValidationError } from './api_keys'; diff --git a/x-pack/plugins/security/server/authentication/authentication_service.test.ts b/x-pack/plugins/security/server/authentication/authentication_service.test.ts index 4e81f0e4a5f13..a779d30891b86 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.test.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.test.ts @@ -36,6 +36,7 @@ import { } from '@kbn/core/server/mocks'; import { customBrandingServiceMock } from '@kbn/core-custom-branding-server-mocks'; import type { UnauthorizedError } from '@kbn/es-errors'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { AuthenticationResult } from './authentication_result'; @@ -43,7 +44,6 @@ import { AuthenticationService } from './authentication_service'; import type { AuthenticatedUser, SecurityLicense } from '../../common'; import { licenseMock } from '../../common/licensing/index.mock'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; -import type { AuditServiceSetup } from '../audit'; import { auditServiceMock } from '../audit/mocks'; import type { ConfigType } from '../config'; import { ConfigSchema, createConfig } from '../config'; diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index a26ac8943ee78..d6f955b8b4558 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -16,6 +16,10 @@ import type { LoggerFactory, } from '@kbn/core/server'; import type { KibanaFeature } from '@kbn/features-plugin/server'; +import type { + AuditServiceSetup, + AuthenticationServiceStart, +} from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { APIKeys } from './api_keys'; @@ -28,7 +32,6 @@ import { renderUnauthenticatedPage } from './unauthenticated_page'; import type { AuthenticatedUser, SecurityLicense } from '../../common'; import { NEXT_URL_QUERY_STRING_PARAMETER } from '../../common/constants'; import { shouldProviderUseLoginForm } from '../../common/model'; -import type { AuditServiceSetup } from '../audit'; import type { ConfigType } from '../config'; import { getDetailedErrorMessage, getErrorStatusCode } from '../errors'; import type { SecurityFeatureUsageServiceStart } from '../feature_usage'; @@ -78,23 +81,6 @@ export interface InternalAuthenticationServiceStart extends AuthenticationServic getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; } -/** - * Authentication services available on the security plugin's start contract. - */ -export interface AuthenticationServiceStart { - apiKeys: Pick< - APIKeys, - | 'areAPIKeysEnabled' - | 'areCrossClusterAPIKeysEnabled' - | 'create' - | 'invalidate' - | 'validate' - | 'grantAsInternalUser' - | 'invalidateAsInternalUser' - >; - getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; -} - export class AuthenticationService { private license!: SecurityLicense; private authenticator?: Authenticator; diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index fbc31e588dc51..c2f6215380985 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -18,6 +18,7 @@ import { httpServiceMock, loggingSystemMock, } from '@kbn/core/server/mocks'; +import type { AuditLogger } from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { AuthenticationResult } from './authentication_result'; @@ -38,7 +39,6 @@ import { import { licenseMock } from '../../common/licensing/index.mock'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; import { userProfileMock } from '../../common/model/user_profile.mock'; -import type { AuditLogger } from '../audit'; import { auditLoggerMock, auditServiceMock } from '../audit/mocks'; import { ConfigSchema, createConfig } from '../config'; import { securityFeatureUsageServiceMock } from '../feature_usage/index.mock'; diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index 032512cc5bf6c..b352b13b97741 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -8,6 +8,7 @@ import type { IBasePath, IClusterClient, KibanaRequest, LoggerFactory } from '@kbn/core/server'; import { CoreKibanaRequest } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { AuthenticationResult } from './authentication_result'; @@ -40,7 +41,6 @@ import { SESSION_ERROR_REASON_HEADER, } from '../../common/constants'; import { shouldProviderUseLoginForm } from '../../common/model'; -import type { AuditServiceSetup } from '../audit'; import { accessAgreementAcknowledgedEvent, userLoginEvent, userLogoutEvent } from '../audit'; import type { ConfigType } from '../config'; import { getErrorStatusCode } from '../errors'; diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index e207b316922dd..e3dabdae4d468 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -6,10 +6,7 @@ */ export { canRedirectRequest } from './can_redirect_request'; -export type { - AuthenticationServiceStart, - InternalAuthenticationServiceStart, -} from './authentication_service'; +export type { InternalAuthenticationServiceStart } from './authentication_service'; export { AuthenticationService } from './authentication_service'; export { AuthenticationResult } from './authentication_result'; export { DeauthenticationResult } from './deauthentication_result'; @@ -27,14 +24,3 @@ export { BasicHTTPAuthorizationHeaderCredentials, HTTPAuthorizationHeader, } from './http_authentication'; -export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - InvalidateAPIKeyResult, - InvalidateAPIKeysParams, - ValidateAPIKeyParams, - GrantAPIKeyResult, -} from './api_keys'; diff --git a/x-pack/plugins/security/server/authentication/providers/base.ts b/x-pack/plugins/security/server/authentication/providers/base.ts index ccf9ecba71f36..8d491a3446f1a 100644 --- a/x-pack/plugins/security/server/authentication/providers/base.ts +++ b/x-pack/plugins/security/server/authentication/providers/base.ts @@ -15,7 +15,7 @@ import type { import { deepFreeze } from '@kbn/std'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { AuthenticatedUser } from '../../../common/model'; +import type { AuthenticatedUser } from '../../../common'; import type { AuthenticationInfo } from '../../elasticsearch'; import { AuthenticationResult } from '../authentication_result'; import type { DeauthenticationResult } from '../deauthentication_result'; diff --git a/x-pack/plugins/security/server/authorization/actions/actions.ts b/x-pack/plugins/security/server/authorization/actions/actions.ts index 073b616c4d775..3b5109c08e3b9 100644 --- a/x-pack/plugins/security/server/authorization/actions/actions.ts +++ b/x-pack/plugins/security/server/authorization/actions/actions.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { Actions as ActionsType } from '@kbn/security-plugin-types-server'; + import { AlertingActions } from './alerting'; import { ApiActions } from './api'; import { AppActions } from './app'; @@ -17,7 +19,7 @@ import { UIActions } from './ui'; * application privileges, and are used to perform the authorization checks implemented * by the various `checkPrivilegesWithRequest` derivatives. */ -export class Actions { +export class Actions implements ActionsType { public readonly api: ApiActions; public readonly app: AppActions; public readonly cases: CasesActions; diff --git a/x-pack/plugins/security/server/authorization/actions/alerting.ts b/x-pack/plugins/security/server/authorization/actions/alerting.ts index 9e4b62338c6fe..c1de9a1c65d21 100644 --- a/x-pack/plugins/security/server/authorization/actions/alerting.ts +++ b/x-pack/plugins/security/server/authorization/actions/alerting.ts @@ -7,7 +7,9 @@ import { isString } from 'lodash'; -export class AlertingActions { +import type { AlertingActions as AlertingActionsType } from '@kbn/security-plugin-types-server'; + +export class AlertingActions implements AlertingActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/api.ts b/x-pack/plugins/security/server/authorization/actions/api.ts index ac6f88dbff965..fec6296d8f63f 100644 --- a/x-pack/plugins/security/server/authorization/actions/api.ts +++ b/x-pack/plugins/security/server/authorization/actions/api.ts @@ -7,7 +7,9 @@ import { isString } from 'lodash'; -export class ApiActions { +import type { ApiActions as ApiActionsType } from '@kbn/security-plugin-types-server'; + +export class ApiActions implements ApiActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/app.ts b/x-pack/plugins/security/server/authorization/actions/app.ts index c18b4f5952498..1aa31b1c1a552 100644 --- a/x-pack/plugins/security/server/authorization/actions/app.ts +++ b/x-pack/plugins/security/server/authorization/actions/app.ts @@ -7,7 +7,9 @@ import { isString } from 'lodash'; -export class AppActions { +import type { AppActions as AppActionsType } from '@kbn/security-plugin-types-server'; + +export class AppActions implements AppActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/cases.ts b/x-pack/plugins/security/server/authorization/actions/cases.ts index ced07c03ebf85..8a0b22b92fc13 100644 --- a/x-pack/plugins/security/server/authorization/actions/cases.ts +++ b/x-pack/plugins/security/server/authorization/actions/cases.ts @@ -7,7 +7,9 @@ import { isString } from 'lodash'; -export class CasesActions { +import type { CasesActions as CasesActionsType } from '@kbn/security-plugin-types-server'; + +export class CasesActions implements CasesActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/saved_object.ts b/x-pack/plugins/security/server/authorization/actions/saved_object.ts index 2f7f043d2a479..05eaa95c0e3ca 100644 --- a/x-pack/plugins/security/server/authorization/actions/saved_object.ts +++ b/x-pack/plugins/security/server/authorization/actions/saved_object.ts @@ -7,7 +7,9 @@ import { isString } from 'lodash'; -export class SavedObjectActions { +import type { SavedObjectActions as SavedObjectActionsType } from '@kbn/security-plugin-types-server'; + +export class SavedObjectActions implements SavedObjectActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/space.ts b/x-pack/plugins/security/server/authorization/actions/space.ts index 6f5a60e05e02e..59cc4e41454ac 100644 --- a/x-pack/plugins/security/server/authorization/actions/space.ts +++ b/x-pack/plugins/security/server/authorization/actions/space.ts @@ -4,8 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { SpaceActions as SpaceActionsType } from '@kbn/security-plugin-types-server'; -export class SpaceActions { +export class SpaceActions implements SpaceActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/actions/ui.ts b/x-pack/plugins/security/server/authorization/actions/ui.ts index 38af89d143c13..2c9986e1c8ce5 100644 --- a/x-pack/plugins/security/server/authorization/actions/ui.ts +++ b/x-pack/plugins/security/server/authorization/actions/ui.ts @@ -9,8 +9,9 @@ import { isString } from 'lodash'; import type { Capabilities as UICapabilities } from '@kbn/core/server'; import { uiCapabilitiesRegex } from '@kbn/features-plugin/server'; +import type { UIActions as UIActionsType } from '@kbn/security-plugin-types-server'; -export class UIActions { +export class UIActions implements UIActionsType { private readonly prefix: string; constructor() { diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index 2c1bc6a5523f3..6956a91d81265 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -6,8 +6,7 @@ */ import type { HttpServiceSetup, Logger } from '@kbn/core/server'; - -import type { AuthorizationServiceSetup } from './authorization_service'; +import type { AuthorizationServiceSetup } from '@kbn/security-plugin-types-server'; export function initAPIAuthorization( http: HttpServiceSetup, diff --git a/x-pack/plugins/security/server/authorization/app_authorization.ts b/x-pack/plugins/security/server/authorization/app_authorization.ts index 08630efc62241..d37d2ec2f37ad 100644 --- a/x-pack/plugins/security/server/authorization/app_authorization.ts +++ b/x-pack/plugins/security/server/authorization/app_authorization.ts @@ -7,8 +7,7 @@ import type { HttpServiceSetup, Logger } from '@kbn/core/server'; import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; - -import type { AuthorizationServiceSetup } from './authorization_service'; +import type { AuthorizationServiceSetup } from '@kbn/security-plugin-types-server'; class ProtectedApplications { private applications: Set | null = null; diff --git a/x-pack/plugins/security/server/authorization/authorization_service.tsx b/x-pack/plugins/security/server/authorization/authorization_service.tsx index 10bee6309438f..16f2ed3b446e1 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.tsx +++ b/x-pack/plugins/security/server/authorization/authorization_service.tsx @@ -25,34 +25,35 @@ import type { PluginSetupContract as FeaturesPluginSetup, PluginStartContract as FeaturesPluginStart, } from '@kbn/features-plugin/server'; +import type { + AuthorizationMode, + AuthorizationServiceSetup, + CheckPrivilegesDynamicallyWithRequest, + CheckSavedObjectsPrivilegesWithRequest, + CheckUserProfilesPrivileges, +} from '@kbn/security-plugin-types-server'; import { Actions } from './actions'; import { initAPIAuthorization } from './api_authorization'; import { initAppAuthorization } from './app_authorization'; import { checkPrivilegesFactory } from './check_privileges'; -import type { CheckPrivilegesDynamicallyWithRequest } from './check_privileges_dynamically'; import { checkPrivilegesDynamicallyWithRequestFactory } from './check_privileges_dynamically'; -import type { CheckSavedObjectsPrivilegesWithRequest } from './check_saved_objects_privileges'; import { checkSavedObjectsPrivilegesWithRequestFactory } from './check_saved_objects_privileges'; import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; -import type { AuthorizationMode } from './mode'; import { authorizationModeFactory } from './mode'; import type { PrivilegesService } from './privileges'; import { privilegesFactory } from './privileges'; import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; import { ResetSessionPage } from './reset_session_page'; -import type { CheckPrivilegesWithRequest, CheckUserProfilesPrivileges } from './types'; import { validateFeaturePrivileges } from './validate_feature_privileges'; import { validateReservedPrivileges } from './validate_reserved_privileges'; +import type { AuthenticatedUser, SecurityLicense } from '../../common'; import { APPLICATION_PREFIX } from '../../common/constants'; -import type { SecurityLicense } from '../../common/licensing'; -import type { AuthenticatedUser } from '../../common/model'; import { canRedirectRequest } from '../authentication'; import type { OnlineStatusRetryScheduler } from '../elasticsearch'; import type { SpacesService } from '../plugin'; export { Actions } from './actions'; -export type { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; interface AuthorizationServiceSetupParams { packageVersion: string; @@ -88,22 +89,6 @@ export interface AuthorizationServiceSetupInternal extends AuthorizationServiceS privileges: PrivilegesService; } -/** - * Authorization services available on the setup contract of the security plugin. - */ -export interface AuthorizationServiceSetup { - /** - * Actions are used to create the "actions" that are associated with Elasticsearch's - * application privileges, and are used to perform the authorization checks implemented - * by the various `checkPrivilegesWithRequest` derivatives. - */ - actions: Actions; - checkPrivilegesWithRequest: CheckPrivilegesWithRequest; - checkPrivilegesDynamicallyWithRequest: CheckPrivilegesDynamicallyWithRequest; - checkSavedObjectsPrivilegesWithRequest: CheckSavedObjectsPrivilegesWithRequest; - mode: AuthorizationMode; -} - export class AuthorizationService { private logger!: Logger; private applicationName!: string; diff --git a/x-pack/plugins/security/server/authorization/check_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_privileges.test.ts index e339645f76dfd..c4da8a95ae46f 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.test.ts @@ -8,10 +8,10 @@ import { uniq } from 'lodash'; import { elasticsearchServiceMock, httpServerMock } from '@kbn/core/server/mocks'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; +import type { HasPrivilegesResponse } from '@kbn/security-plugin-types-server'; import { checkPrivilegesFactory } from './check_privileges'; -import type { HasPrivilegesResponse } from './types'; -import { GLOBAL_RESOURCE } from '../../common/constants'; const application = 'kibana-our_application'; diff --git a/x-pack/plugins/security/server/authorization/check_privileges.ts b/x-pack/plugins/security/server/authorization/check_privileges.ts index abc0b791dd49a..310b79f362824 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.ts @@ -9,8 +9,6 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import { pick, transform, uniq } from 'lodash'; import type { IClusterClient, KibanaRequest } from '@kbn/core/server'; - -import { ResourceSerializer } from './resource_serializer'; import type { CheckPrivileges, CheckPrivilegesOptions, @@ -21,9 +19,11 @@ import type { CheckUserProfilesPrivilegesResponse, HasPrivilegesResponse, HasPrivilegesResponseApplication, -} from './types'; +} from '@kbn/security-plugin-types-server'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; + +import { ResourceSerializer } from './resource_serializer'; import { validateEsPrivilegeResponse } from './validate_es_response'; -import { GLOBAL_RESOURCE } from '../../common/constants'; interface CheckPrivilegesActions { login: string; diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts index 2d506a70610d3..6b92f6cef748f 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts @@ -6,9 +6,9 @@ */ import { httpServerMock } from '@kbn/core/server/mocks'; +import type { CheckPrivilegesOptions } from '@kbn/security-plugin-types-server'; import { checkPrivilegesDynamicallyWithRequestFactory } from './check_privileges_dynamically'; -import type { CheckPrivilegesOptions } from './types'; test(`checkPrivileges.atSpace when spaces is enabled`, async () => { const expectedResult = Symbol(); diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts index 22c2e53e9ab27..7e84dad2775ac 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts @@ -6,23 +6,14 @@ */ import type { KibanaRequest } from '@kbn/core/server'; - import type { + CheckPrivilegesDynamicallyWithRequest, CheckPrivilegesOptions, CheckPrivilegesPayload, - CheckPrivilegesResponse, CheckPrivilegesWithRequest, -} from './types'; -import type { SpacesService } from '../plugin'; +} from '@kbn/security-plugin-types-server'; -export type CheckPrivilegesDynamically = ( - privileges: CheckPrivilegesPayload, - options?: CheckPrivilegesOptions -) => Promise; - -export type CheckPrivilegesDynamicallyWithRequest = ( - request: KibanaRequest -) => CheckPrivilegesDynamically; +import type { SpacesService } from '../plugin'; export function checkPrivilegesDynamicallyWithRequestFactory( checkPrivilegesWithRequest: CheckPrivilegesWithRequest, diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts index 0afcd4118ab8b..3072df7d269ec 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts @@ -6,9 +6,12 @@ */ import { httpServerMock } from '@kbn/core/server/mocks'; +import type { + CheckPrivileges, + CheckPrivilegesWithRequest, +} from '@kbn/security-plugin-types-server'; import { checkSavedObjectsPrivilegesWithRequestFactory } from './check_saved_objects_privileges'; -import type { CheckPrivileges, CheckPrivilegesWithRequest } from './types'; import type { SpacesService } from '../plugin'; let mockCheckPrivileges: jest.Mocked; diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts index 0afa29fab3c58..2e27197d6c952 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts @@ -6,20 +6,15 @@ */ import type { KibanaRequest } from '@kbn/core/server'; +import type { + CheckPrivilegesWithRequest, + CheckSavedObjectsPrivileges, + CheckSavedObjectsPrivilegesWithRequest, +} from '@kbn/security-plugin-types-server'; -import type { CheckPrivilegesResponse, CheckPrivilegesWithRequest } from './types'; import { ALL_SPACES_ID } from '../../common/constants'; import type { SpacesService } from '../plugin'; -export type CheckSavedObjectsPrivilegesWithRequest = ( - request: KibanaRequest -) => CheckSavedObjectsPrivileges; - -export type CheckSavedObjectsPrivileges = ( - actions: string | string[], - namespaceOrNamespaces?: string | Array -) => Promise; - function uniq(arr: T[]): T[] { return Array.from(new Set(arr)); } diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts index 8f56ba95883b5..3d784f6c1cf3f 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts @@ -7,12 +7,12 @@ import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { ElasticsearchFeature, KibanaFeature } from '@kbn/features-plugin/server'; +import type { CheckPrivilegesResponse } from '@kbn/security-plugin-types-server'; import { Actions } from './actions'; import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; import { authorizationMock } from './index.mock'; -import type { CheckPrivilegesResponse } from './types'; -import type { AuthenticatedUser } from '../../common/model'; +import type { AuthenticatedUser } from '../../common'; type MockAuthzOptions = | { rejectCheckPrivileges: any } diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts index 7283b955e906c..cf2429a1d657d 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts @@ -14,11 +14,13 @@ import type { FeatureElasticsearchPrivileges, KibanaFeature, } from '@kbn/features-plugin/server'; +import type { + AuthorizationServiceSetup, + CheckPrivilegesResponse, +} from '@kbn/security-plugin-types-server'; import type { RecursiveReadonly, RecursiveReadonlyArray } from '@kbn/utility-types'; -import type { AuthorizationServiceSetup } from './authorization_service'; -import type { CheckPrivilegesResponse } from './types'; -import type { AuthenticatedUser } from '../../common/model'; +import type { AuthenticatedUser } from '../../common'; export function disableUICapabilitiesFactory( request: KibanaRequest, diff --git a/x-pack/plugins/security/server/authorization/index.mock.ts b/x-pack/plugins/security/server/authorization/index.mock.ts index f66104818ae8f..04c389f24fcad 100644 --- a/x-pack/plugins/security/server/authorization/index.mock.ts +++ b/x-pack/plugins/security/server/authorization/index.mock.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { AuthorizationMode } from '@kbn/security-plugin-types-server'; + import { actionsMock } from './actions/actions.mock'; -import type { AuthorizationMode } from './mode'; export const authorizationMock = { create: ({ diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index 9e701e866767b..2a93aeb070011 100644 --- a/x-pack/plugins/security/server/authorization/index.ts +++ b/x-pack/plugins/security/server/authorization/index.ts @@ -6,13 +6,8 @@ */ export { Actions } from './actions'; -export type { - AuthorizationServiceSetup, - AuthorizationServiceSetupInternal, -} from './authorization_service'; +export type { AuthorizationServiceSetupInternal } from './authorization_service'; export { AuthorizationService } from './authorization_service'; -export type { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; -export type { CheckPrivilegesPayload } from './types'; export type { ElasticsearchRole } from './roles'; export { transformElasticsearchRoleToRole } from './roles'; export type { CasesSupportedOperations } from './privileges'; diff --git a/x-pack/plugins/security/server/authorization/mode.test.ts b/x-pack/plugins/security/server/authorization/mode.test.ts index 2df3d9ab6b5b2..f44d023be8737 100644 --- a/x-pack/plugins/security/server/authorization/mode.test.ts +++ b/x-pack/plugins/security/server/authorization/mode.test.ts @@ -8,9 +8,8 @@ import { httpServerMock } from '@kbn/core/server/mocks'; import { authorizationModeFactory } from './mode'; -import type { SecurityLicense } from '../../common/licensing'; +import type { SecurityLicense, SecurityLicenseFeatures } from '../../common'; import { licenseMock } from '../../common/licensing/index.mock'; -import type { SecurityLicenseFeatures } from '../../common/licensing/license_features'; describe(`#useRbacForRequest`, () => { let mockLicense: jest.Mocked; diff --git a/x-pack/plugins/security/server/authorization/mode.ts b/x-pack/plugins/security/server/authorization/mode.ts index d40d7fba3ca88..722ecd02875e0 100644 --- a/x-pack/plugins/security/server/authorization/mode.ts +++ b/x-pack/plugins/security/server/authorization/mode.ts @@ -7,11 +7,7 @@ import type { KibanaRequest } from '@kbn/core/server'; -import type { SecurityLicense } from '../../common/licensing'; - -export interface AuthorizationMode { - useRbacForRequest(request: KibanaRequest): boolean; -} +import type { SecurityLicense } from '../../common'; export function authorizationModeFactory(license: SecurityLicense) { const useRbacForRequestCache = new WeakMap(); diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/__snapshots__/cases.test.ts.snap b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/__snapshots__/cases.test.ts.snap index fc31927e6cfb5..1874a17515e19 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/__snapshots__/cases.test.ts.snap +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/__snapshots__/cases.test.ts.snap @@ -5,7 +5,6 @@ Array [ "cases:observability/pushCase", "cases:observability/createCase", "cases:observability/createComment", - "cases:observability/createConfiguration", "cases:observability/getCase", "cases:observability/getComment", "cases:observability/getTags", @@ -14,9 +13,10 @@ Array [ "cases:observability/findConfigurations", "cases:observability/updateCase", "cases:observability/updateComment", - "cases:observability/updateConfiguration", "cases:observability/deleteCase", "cases:observability/deleteComment", + "cases:observability/createConfiguration", + "cases:observability/updateConfiguration", ] `; @@ -24,7 +24,6 @@ exports[`cases feature_privilege_builder within feature grants create privileges Array [ "cases:securitySolution/createCase", "cases:securitySolution/createComment", - "cases:securitySolution/createConfiguration", ] `; @@ -52,10 +51,16 @@ Array [ ] `; +exports[`cases feature_privilege_builder within feature grants settings privileges under feature with id observability 1`] = ` +Array [ + "cases:observability/createConfiguration", + "cases:observability/updateConfiguration", +] +`; + exports[`cases feature_privilege_builder within feature grants update privileges under feature with id observability 1`] = ` Array [ "cases:observability/updateCase", "cases:observability/updateComment", - "cases:observability/updateConfiguration", ] `; diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.test.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.test.ts index d4d49a5334f1d..ad0563ef7a827 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.test.ts @@ -47,6 +47,7 @@ describe(`cases`, () => { ['read', 'observability'], ['update', 'observability'], ['delete', 'securitySolution'], + ['settings', 'observability'], ])('grants %s privileges under feature with id %s', (operation, featureID) => { const actions = new Actions(); const casesFeaturePrivilege = new FeaturePrivilegeCasesBuilder(actions); @@ -55,7 +56,6 @@ describe(`cases`, () => { cases: { [operation]: [featureID], }, - savedObject: { all: [], read: [], @@ -88,8 +88,8 @@ describe(`cases`, () => { update: ['obs'], delete: ['security'], read: ['obs'], + settings: ['security'], }, - savedObject: { all: [], read: [], @@ -113,7 +113,6 @@ describe(`cases`, () => { "cases:security/pushCase", "cases:security/createCase", "cases:security/createComment", - "cases:security/createConfiguration", "cases:security/getCase", "cases:security/getComment", "cases:security/getTags", @@ -122,9 +121,10 @@ describe(`cases`, () => { "cases:security/findConfigurations", "cases:security/updateCase", "cases:security/updateComment", - "cases:security/updateConfiguration", "cases:security/deleteCase", "cases:security/deleteComment", + "cases:security/createConfiguration", + "cases:security/updateConfiguration", "cases:obs/getCase", "cases:obs/getComment", "cases:obs/getTags", @@ -133,7 +133,6 @@ describe(`cases`, () => { "cases:obs/findConfigurations", "cases:obs/updateCase", "cases:obs/updateComment", - "cases:obs/updateConfiguration", ] `); }); @@ -147,7 +146,6 @@ describe(`cases`, () => { all: ['security', 'other-security'], read: ['obs', 'other-obs'], }, - savedObject: { all: [], read: [], @@ -171,7 +169,6 @@ describe(`cases`, () => { "cases:security/pushCase", "cases:security/createCase", "cases:security/createComment", - "cases:security/createConfiguration", "cases:security/getCase", "cases:security/getComment", "cases:security/getTags", @@ -180,13 +177,13 @@ describe(`cases`, () => { "cases:security/findConfigurations", "cases:security/updateCase", "cases:security/updateComment", - "cases:security/updateConfiguration", "cases:security/deleteCase", "cases:security/deleteComment", + "cases:security/createConfiguration", + "cases:security/updateConfiguration", "cases:other-security/pushCase", "cases:other-security/createCase", "cases:other-security/createComment", - "cases:other-security/createConfiguration", "cases:other-security/getCase", "cases:other-security/getComment", "cases:other-security/getTags", @@ -195,9 +192,10 @@ describe(`cases`, () => { "cases:other-security/findConfigurations", "cases:other-security/updateCase", "cases:other-security/updateComment", - "cases:other-security/updateConfiguration", "cases:other-security/deleteCase", "cases:other-security/deleteComment", + "cases:other-security/createConfiguration", + "cases:other-security/updateConfiguration", "cases:obs/getCase", "cases:obs/getComment", "cases:obs/getTags", diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts index 0f442c9d871e1..b54ba77777dd8 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts @@ -13,11 +13,16 @@ import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export type CasesSupportedOperations = typeof allOperations[number]; -// if you add a value here you'll likely also need to make changes here: -// x-pack/plugins/cases/server/authorization/index.ts +/** + * If you add a new operation type (all, push, update, etc) you should also + * extend the mapping here x-pack/plugins/features/server/feature_privilege_iterator/feature_privilege_iterator.ts + * + * Also if you add a new operation (createCase, updateCase, etc) here you'll likely also need to make changes here: + * x-pack/plugins/cases/server/authorization/index.ts + */ const pushOperations = ['pushCase'] as const; -const createOperations = ['createCase', 'createComment', 'createConfiguration'] as const; +const createOperations = ['createCase', 'createComment'] as const; const readOperations = [ 'getCase', 'getComment', @@ -26,14 +31,16 @@ const readOperations = [ 'getUserActions', 'findConfigurations', ] as const; -const updateOperations = ['updateCase', 'updateComment', 'updateConfiguration'] as const; +const updateOperations = ['updateCase', 'updateComment'] as const; const deleteOperations = ['deleteCase', 'deleteComment'] as const; +const settingsOperations = ['createConfiguration', 'updateConfiguration'] as const; const allOperations = [ ...pushOperations, ...createOperations, ...readOperations, ...updateOperations, ...deleteOperations, + ...settingsOperations, ] as const; export class FeaturePrivilegeCasesBuilder extends BaseFeaturePrivilegeBuilder { @@ -57,6 +64,7 @@ export class FeaturePrivilegeCasesBuilder extends BaseFeaturePrivilegeBuilder { ...getCasesPrivilege(readOperations, privilegeDefinition.cases?.read), ...getCasesPrivilege(updateOperations, privilegeDefinition.cases?.update), ...getCasesPrivilege(deleteOperations, privilegeDefinition.cases?.delete), + ...getCasesPrivilege(settingsOperations, privilegeDefinition.cases?.settings), ]); } } diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.ts index e3e151052f056..51c61962c946f 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.ts @@ -13,7 +13,7 @@ import type { } from '@kbn/features-plugin/server'; import { featurePrivilegeBuilderFactory } from './feature_privilege_builder'; -import type { SecurityLicense } from '../../../common/licensing'; +import type { SecurityLicense } from '../../../common'; import type { RawKibanaPrivileges } from '../../../common/model'; import type { Actions } from '../actions'; diff --git a/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts b/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts index 2febad56d7016..6a46072712dfc 100644 --- a/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts +++ b/x-pack/plugins/security/server/authorization/roles/elasticsearch_role.ts @@ -7,12 +7,10 @@ import type { Logger } from '@kbn/core/server'; import type { KibanaFeature } from '@kbn/features-plugin/common'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; -import { - GLOBAL_RESOURCE, - RESERVED_PRIVILEGES_APPLICATION_WILDCARD, -} from '../../../common/constants'; -import type { Role, RoleKibanaPrivilege } from '../../../common/model'; +import type { Role, RoleKibanaPrivilege } from '../../../common'; +import { RESERVED_PRIVILEGES_APPLICATION_WILDCARD } from '../../../common/constants'; import { getDetailedErrorMessage } from '../../errors'; import { PrivilegeSerializer } from '../privilege_serializer'; import { ResourceSerializer } from '../resource_serializer'; diff --git a/x-pack/plugins/security/server/authorization/validate_es_response.ts b/x-pack/plugins/security/server/authorization/validate_es_response.ts index 52b1777269f35..3eff39aedf14f 100644 --- a/x-pack/plugins/security/server/authorization/validate_es_response.ts +++ b/x-pack/plugins/security/server/authorization/validate_es_response.ts @@ -6,8 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - -import type { HasPrivilegesResponse } from './types'; +import type { HasPrivilegesResponse } from '@kbn/security-plugin-types-server'; const baseResponseSchema = schema.object({ username: schema.string(), diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index a5483b4e70ba2..68e1c7c2a0964 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -16,7 +16,7 @@ import { config as coreConfig } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { getLogsPath } from '@kbn/utils'; -import type { AuthenticationProvider } from '../common/model'; +import type { AuthenticationProvider } from '../common'; export type ConfigType = ReturnType; type RawConfigType = TypeOf; diff --git a/x-pack/plugins/security/server/deprecations/privilege_deprecations.ts b/x-pack/plugins/security/server/deprecations/privilege_deprecations.ts index c85f6b239d328..8075c258f323f 100644 --- a/x-pack/plugins/security/server/deprecations/privilege_deprecations.ts +++ b/x-pack/plugins/security/server/deprecations/privilege_deprecations.ts @@ -8,12 +8,12 @@ import type { Logger } from '@kbn/core/server'; import type { KibanaFeature } from '@kbn/features-plugin/common'; import { i18n } from '@kbn/i18n'; - -import type { SecurityLicense } from '../../common/licensing'; import type { PrivilegeDeprecationsRolesByFeatureIdRequest, PrivilegeDeprecationsRolesByFeatureIdResponse, -} from '../../common/model'; +} from '@kbn/security-plugin-types-server'; + +import type { SecurityLicense } from '../../common'; import { transformElasticsearchRoleToRole } from '../authorization'; import type { AuthorizationServiceSetupInternal } from '../authorization'; import { getDetailedErrorMessage, getErrorStatusCode } from '../errors'; diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts index 1d9985fbc8650..1dd56fdffec8b 100644 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts +++ b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts @@ -13,7 +13,7 @@ import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { nextTick } from '@kbn/test-jest-helpers'; import { ElasticsearchService } from './elasticsearch_service'; -import type { SecurityLicense, SecurityLicenseFeatures } from '../../common/licensing'; +import type { SecurityLicense, SecurityLicenseFeatures } from '../../common'; import { licenseMock } from '../../common/licensing/index.mock'; describe('ElasticsearchService', () => { diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts index 88fe5caf2cac3..2bc8a643275c1 100644 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts +++ b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts @@ -12,7 +12,7 @@ import { distinctUntilChanged, filter, map, shareReplay, tap } from 'rxjs/operat import type { Logger, StatusServiceSetup } from '@kbn/core/server'; import { ServiceStatusLevels } from '@kbn/core/server'; -import type { SecurityLicense } from '../../common/licensing'; +import type { SecurityLicense } from '../../common'; export interface ElasticsearchServiceSetupParams { readonly status: StatusServiceSetup; diff --git a/x-pack/plugins/security/server/elasticsearch/index.ts b/x-pack/plugins/security/server/elasticsearch/index.ts index 239802028b122..d955587ef0738 100644 --- a/x-pack/plugins/security/server/elasticsearch/index.ts +++ b/x-pack/plugins/security/server/elasticsearch/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuthenticatedUser } from '../../common/model'; +import type { AuthenticatedUser } from '../../common'; export type AuthenticationInfo = Omit< AuthenticatedUser, diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts index 3d5cd022b4c96..d941532aa815f 100644 --- a/x-pack/plugins/security/server/index.ts +++ b/x-pack/plugins/security/server/index.ts @@ -11,41 +11,73 @@ import type { PluginInitializer, PluginInitializerContext, } from '@kbn/core/server'; +import type { SecurityPluginStart } from '@kbn/security-plugin-types-server'; import type { RecursiveReadonly } from '@kbn/utility-types'; import { ConfigSchema } from './config'; import { securityConfigDeprecationProvider } from './config_deprecations'; -import type { PluginSetupDependencies, SecurityPluginSetup, SecurityPluginStart } from './plugin'; +import type { PluginSetupDependencies, SecurityPluginSetup } from './plugin'; // These exports are part of public Security plugin contract, any change in signature of exported // functions or removal of exports should be considered as a breaking change. +export { HTTPAuthorizationHeader } from './authentication'; +export type { CasesSupportedOperations } from './authorization'; +export type { SecurityPluginSetup, SecurityPluginStart }; +export type { AuthenticatedUser } from '../common'; +export { ROUTE_TAG_CAN_REDIRECT } from './routes/tags'; + +// Re-export types from the plugin directly to enhance the developer experience for consumers of the Security plugin. export type { - CreateAPIKeyParams, + AuditEvent, + AuditHttp, + AuditKibana, + AuditRequest, + AuditLogger, + AuditServiceSetup, + APIKeys, + AuthenticationServiceStart, + InvalidateAPIKeyResult, + GrantAPIKeyResult, + ValidateAPIKeyParams, CreateAPIKeyResult, + InvalidateAPIKeysParams, + CreateAPIKeyParams, CreateRestAPIKeyParams, CreateRestAPIKeyWithKibanaPrivilegesParams, CreateCrossClusterAPIKeyParams, - InvalidateAPIKeysParams, - InvalidateAPIKeyResult, - GrantAPIKeyResult, - ValidateAPIKeyParams, - AuthenticationServiceStart, -} from './authentication'; -export { HTTPAuthorizationHeader } from './authentication'; -export type { CheckPrivilegesPayload, CasesSupportedOperations } from './authorization'; -export type AuthorizationServiceSetup = SecurityPluginStart['authz']; -export type { AuditLogger, AuditEvent, AuditHttp, AuditKibana, AuditRequest } from './audit'; -export type { SecurityPluginSetup, SecurityPluginStart }; -export type { AuthenticatedUser } from '../common/model'; -export { ROUTE_TAG_CAN_REDIRECT } from './routes/tags'; -export type { AuditServiceSetup } from './audit'; -export type { - UserProfileServiceStart, + Actions, + AlertingActions, + ApiActions, + AppActions, + CasesActions, + SavedObjectActions, + SpaceActions, + UIActions, + AuthorizationServiceSetup, + CheckPrivileges, + CheckPrivilegesPayload, + CheckUserProfilesPrivileges, + CheckPrivilegesDynamically, + CheckPrivilegesDynamicallyWithRequest, + CheckUserProfilesPrivilegesResponse, + CheckUserProfilesPrivilegesPayload, + CheckPrivilegesOptions, + CheckPrivilegesResponse, + CheckPrivilegesWithRequest, + CheckSavedObjectsPrivileges, + CheckSavedObjectsPrivilegesWithRequest, + ElasticsearchPrivilegesType, + KibanaPrivilegesType, + AuthorizationMode, + PrivilegeDeprecationsRolesByFeatureIdResponse, + PrivilegeDeprecationsService, + PrivilegeDeprecationsRolesByFeatureIdRequest, UserProfileBulkGetParams, UserProfileSuggestParams, UserProfileRequiredPrivileges, UserProfileGetCurrentParams, -} from './user_profile'; + UserProfileServiceStart, +} from '@kbn/security-plugin-types-server'; export const config: PluginConfigDescriptor> = { schema: ConfigSchema, diff --git a/x-pack/plugins/security/server/lib/index.ts b/x-pack/plugins/security/server/lib/index.ts index 715eeb0955daa..496f60fdb098c 100644 --- a/x-pack/plugins/security/server/lib/index.ts +++ b/x-pack/plugins/security/server/lib/index.ts @@ -5,8 +5,6 @@ * 2.0. */ -export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './role_schema'; -export { elasticsearchRoleSchema, getKibanaRoleSchema } from './role_schema'; export { validateKibanaPrivileges, transformPrivilegesToElasticsearchPrivileges, diff --git a/x-pack/plugins/security/server/lib/role_utils.ts b/x-pack/plugins/security/server/lib/role_utils.ts index ff7a690293443..1df5254a38df0 100644 --- a/x-pack/plugins/security/server/lib/role_utils.ts +++ b/x-pack/plugins/security/server/lib/role_utils.ts @@ -6,9 +6,10 @@ */ import type { KibanaFeature } from '@kbn/features-plugin/server'; +import type { KibanaPrivilegesType } from '@kbn/security-plugin-types-server'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; -import type { KibanaPrivilegesType } from './role_schema'; -import { ALL_SPACES_ID, GLOBAL_RESOURCE } from '../../common/constants'; +import { ALL_SPACES_ID } from '../../common/constants'; import { PrivilegeSerializer } from '../authorization/privilege_serializer'; import { ResourceSerializer } from '../authorization/resource_serializer'; diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 341ab79e97e5b..adfed1bad88e9 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -23,6 +23,12 @@ import type { PluginStartContract as FeaturesPluginStart, } from '@kbn/features-plugin/server'; import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/server'; +import type { + AuditServiceSetup, + AuthorizationServiceSetup, + SecurityPluginSetup as SecurityPluginSetupWithoutDeprecatedMembers, + SecurityPluginStart, +} from '@kbn/security-plugin-types-server'; import type { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; import type { TaskManagerSetupContract, @@ -33,14 +39,10 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { AnalyticsService } from './analytics'; import type { AnonymousAccessServiceStart } from './anonymous_access'; import { AnonymousAccessService } from './anonymous_access'; -import type { AuditServiceSetup } from './audit'; import { AuditService } from './audit'; -import type { - AuthenticationServiceStart, - InternalAuthenticationServiceStart, -} from './authentication'; +import type { InternalAuthenticationServiceStart } from './authentication'; import { AuthenticationService } from './authentication'; -import type { AuthorizationServiceSetup, AuthorizationServiceSetupInternal } from './authorization'; +import type { AuthorizationServiceSetupInternal } from './authorization'; import { AuthorizationService } from './authorization'; import type { ConfigSchema, ConfigType } from './config'; import { createConfig } from './config'; @@ -56,11 +58,11 @@ import { SessionManagementService } from './session_management'; import { setupSpacesClient } from './spaces'; import { registerSecurityUsageCollector } from './usage_collector'; import { UserProfileService } from './user_profile'; -import type { UserProfileServiceStart, UserProfileServiceStartInternal } from './user_profile'; +import type { UserProfileServiceStartInternal } from './user_profile'; import { UserProfileSettingsClient } from './user_profile/user_profile_settings_client'; import type { UserSettingServiceStart } from './user_profile/user_setting_service'; import { UserSettingService } from './user_profile/user_setting_service'; -import type { AuthenticatedUser, PrivilegeDeprecationsService, SecurityLicense } from '../common'; +import type { AuthenticatedUser, SecurityLicense } from '../common'; import { SecurityLicenseService } from '../common/licensing'; export type SpacesService = Pick< @@ -71,7 +73,7 @@ export type SpacesService = Pick< /** * Describes public Security plugin contract returned at the `setup` stage. */ -export interface SecurityPluginSetup { +export interface SecurityPluginSetup extends SecurityPluginSetupWithoutDeprecatedMembers { /** * @deprecated Use `authc` methods from the `SecurityServiceStart` contract instead. */ @@ -80,36 +82,6 @@ export interface SecurityPluginSetup { * @deprecated Use `authz` methods from the `SecurityServiceStart` contract instead. */ authz: AuthorizationServiceSetup; - /** - * Exposes information about the available security features under the current license. - */ - license: SecurityLicense; - /** - * Exposes services for audit logging. - */ - audit: AuditServiceSetup; - /** - * Exposes services to access kibana roles per feature id with the GetDeprecationsContext - */ - privilegeDeprecationsService: PrivilegeDeprecationsService; -} - -/** - * Describes public Security plugin contract returned at the `start` stage. - */ -export interface SecurityPluginStart { - /** - * Authentication services to confirm the user is who they say they are. - */ - authc: AuthenticationServiceStart; - /** - * Authorization services to manage and access the permissions a particular user has. - */ - authz: AuthorizationServiceSetup; - /** - * User profiles services to retrieve user profiles. - */ - userProfiles: UserProfileServiceStart; } export interface PluginSetupDependencies { diff --git a/x-pack/plugins/security/server/routes/api_keys/create.ts b/x-pack/plugins/security/server/routes/api_keys/create.ts index ee69a80efa103..59d743e3726aa 100644 --- a/x-pack/plugins/security/server/routes/api_keys/create.ts +++ b/x-pack/plugins/security/server/routes/api_keys/create.ts @@ -5,84 +5,18 @@ * 2.0. */ -import type { estypes } from '@elastic/elasticsearch'; - import { schema } from '@kbn/config-schema'; -import type { TypeOf } from '@kbn/config-schema'; +import { + crossClusterApiKeySchema, + getRestApiKeyWithKibanaPrivilegesSchema, + restApiKeySchema, +} from '@kbn/security-plugin-types-server'; import type { RouteDefinitionParams } from '..'; import { CreateApiKeyValidationError } from '../../authentication/api_keys'; import { wrapIntoCustomErrorResponse } from '../../errors'; -import { elasticsearchRoleSchema, getKibanaRoleSchema } from '../../lib'; import { createLicensedRouteHandler } from '../licensed_route_handler'; -/** - * Response of Kibana Create API key endpoint. - */ -export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse; - -/** - * Request body of Kibana Create API key endpoint. - */ -export type CreateAPIKeyParams = - | CreateRestAPIKeyParams - | CreateRestAPIKeyWithKibanaPrivilegesParams - | CreateCrossClusterAPIKeyParams; - -export type CreateRestAPIKeyParams = TypeOf; -export type CreateRestAPIKeyWithKibanaPrivilegesParams = TypeOf< - ReturnType ->; -export type CreateCrossClusterAPIKeyParams = TypeOf; - -export const restApiKeySchema = schema.object({ - type: schema.maybe(schema.literal('rest')), - name: schema.string(), - expiration: schema.maybe(schema.string()), - role_descriptors: schema.recordOf(schema.string(), schema.object({}, { unknowns: 'allow' }), { - defaultValue: {}, - }), - metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), -}); - -export const getRestApiKeyWithKibanaPrivilegesSchema = ( - getBasePrivilegeNames: Parameters[0] -) => - restApiKeySchema.extends({ - role_descriptors: null, - kibana_role_descriptors: schema.recordOf( - schema.string(), - schema.object({ - elasticsearch: elasticsearchRoleSchema.extends({}, { unknowns: 'allow' }), - kibana: getKibanaRoleSchema(getBasePrivilegeNames), - }) - ), - }); - -export const crossClusterApiKeySchema = restApiKeySchema.extends({ - type: schema.literal('cross_cluster'), - role_descriptors: null, - access: schema.object( - { - search: schema.maybe( - schema.arrayOf( - schema.object({ - names: schema.arrayOf(schema.string()), - }) - ) - ), - replication: schema.maybe( - schema.arrayOf( - schema.object({ - names: schema.arrayOf(schema.string()), - }) - ) - ), - }, - { unknowns: 'allow' } - ), -}); - export function defineCreateApiKeyRoutes({ router, authz, diff --git a/x-pack/plugins/security/server/routes/api_keys/index.ts b/x-pack/plugins/security/server/routes/api_keys/index.ts index 9f086afcfd248..9855d94923c33 100644 --- a/x-pack/plugins/security/server/routes/api_keys/index.ts +++ b/x-pack/plugins/security/server/routes/api_keys/index.ts @@ -12,13 +12,6 @@ import { defineInvalidateApiKeysRoutes } from './invalidate'; import { defineUpdateApiKeyRoutes } from './update'; import type { RouteDefinitionParams } from '..'; -export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - CreateCrossClusterAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, -} from './create'; export type { UpdateAPIKeyParams, UpdateAPIKeyResult, diff --git a/x-pack/plugins/security/server/routes/api_keys/update.ts b/x-pack/plugins/security/server/routes/api_keys/update.ts index 0a05ffe048205..ef999820c6cae 100644 --- a/x-pack/plugins/security/server/routes/api_keys/update.ts +++ b/x-pack/plugins/security/server/routes/api_keys/update.ts @@ -9,11 +9,11 @@ import type { estypes } from '@elastic/elasticsearch'; import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; +import { elasticsearchRoleSchema, getKibanaRoleSchema } from '@kbn/security-plugin-types-server'; import type { RouteDefinitionParams } from '..'; import { UpdateApiKeyValidationError } from '../../authentication/api_keys/api_keys'; import { wrapIntoCustomErrorResponse } from '../../errors'; -import { elasticsearchRoleSchema, getKibanaRoleSchema } from '../../lib'; import { createLicensedRouteHandler } from '../licensed_route_handler'; /** diff --git a/x-pack/plugins/security/server/routes/authentication/common.test.ts b/x-pack/plugins/security/server/routes/authentication/common.test.ts index 44b56b73b220d..6abf1b445b98a 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.test.ts @@ -12,7 +12,7 @@ import { httpServerMock } from '@kbn/core/server/mocks'; import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import { defineCommonRoutes } from './common'; -import type { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; +import type { SecurityLicense, SecurityLicenseFeatures } from '../../../common'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; import type { InternalAuthenticationServiceStart } from '../../authentication'; import { diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts index 5c8a07d15000d..6bd8e5a4ec70a 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts @@ -7,13 +7,10 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; +import { elasticsearchRoleSchema, getKibanaRoleSchema } from '@kbn/security-plugin-types-server'; import type { ElasticsearchRole } from '../../../../authorization'; -import { - elasticsearchRoleSchema, - getKibanaRoleSchema, - transformPrivilegesToElasticsearchPrivileges, -} from '../../../../lib'; +import { transformPrivilegesToElasticsearchPrivileges } from '../../../../lib'; export const transformPutPayloadToElasticsearchRole = ( rolePayload: RolePayloadSchemaType, diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index 77e3bd51b5e80..18a07bce0a23e 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -10,9 +10,9 @@ import { kibanaResponseFactory } from '@kbn/core/server'; import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import { KibanaFeature } from '@kbn/features-plugin/server'; import type { LicenseCheck } from '@kbn/licensing-plugin/server'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; import { definePutRolesRoutes } from './put'; -import { GLOBAL_RESOURCE } from '../../../../common/constants'; import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock'; import { routeDefinitionParamsMock } from '../../index.mock'; diff --git a/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts b/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts index 8329be6a91862..c518030a76f1c 100644 --- a/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts @@ -8,11 +8,11 @@ import type { RequestHandler, RouteConfig } from '@kbn/core/server'; import { kibanaResponseFactory } from '@kbn/core/server'; import { httpServerMock } from '@kbn/core/server/mocks'; +import type { CheckPrivileges } from '@kbn/security-plugin-types-server'; import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import { defineShareSavedObjectPermissionRoutes } from './share_saved_object_permissions'; import type { RouteDefinitionParams } from '../..'; -import type { CheckPrivileges } from '../../../authorization/types'; import type { SecurityRequestHandlerContext, SecurityRouter } from '../../../types'; import { routeDefinitionParamsMock } from '../../index.mock'; diff --git a/x-pack/plugins/security/server/routes/security_checkup/get_state.test.ts b/x-pack/plugins/security/server/routes/security_checkup/get_state.test.ts index c40f0b92b54a2..3a7f89589c846 100644 --- a/x-pack/plugins/security/server/routes/security_checkup/get_state.test.ts +++ b/x-pack/plugins/security/server/routes/security_checkup/get_state.test.ts @@ -15,7 +15,7 @@ import { kibanaResponseFactory } from '@kbn/core/server'; import { httpServerMock } from '@kbn/core/server/mocks'; import { defineSecurityCheckupGetStateRoutes } from './get_state'; -import type { SecurityLicenseFeatures } from '../../../common/licensing'; +import type { SecurityLicenseFeatures } from '../../../common'; import { licenseMock } from '../../../common/licensing/index.mock'; import { routeDefinitionParamsMock, securityRequestHandlerContextMock } from '../index.mock'; diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts index 6190f03e6ed78..ef588ae1cfcfc 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts @@ -16,8 +16,11 @@ import { httpResourcesMock, httpServerMock } from '@kbn/core/server/mocks'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { defineAccessAgreementRoutes } from './access_agreement'; -import type { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; -import type { AuthenticationProvider } from '../../../common/model'; +import type { + AuthenticationProvider, + SecurityLicense, + SecurityLicenseFeatures, +} from '../../../common'; import type { ConfigType } from '../../config'; import type { Session } from '../../session_management'; import { sessionMock } from '../../session_management/session.mock'; diff --git a/x-pack/plugins/security/server/routes/views/login.test.ts b/x-pack/plugins/security/server/routes/views/login.test.ts index b73b048d9f4d3..086c0c785e6bc 100644 --- a/x-pack/plugins/security/server/routes/views/login.test.ts +++ b/x-pack/plugins/security/server/routes/views/login.test.ts @@ -18,7 +18,7 @@ import { kibanaResponseFactory } from '@kbn/core/server'; import { coreMock, httpResourcesMock, httpServerMock } from '@kbn/core/server/mocks'; import { defineLoginRoutes } from './login'; -import type { SecurityLicense } from '../../../common/licensing'; +import type { SecurityLicense } from '../../../common'; import type { LoginSelectorProvider } from '../../../common/login_state'; import type { ConfigType } from '../../config'; import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; diff --git a/x-pack/plugins/security/server/saved_objects/ensure_authorized.test.ts b/x-pack/plugins/security/server/saved_objects/ensure_authorized.test.ts index 2c7799eae5261..22c219a5668ab 100644 --- a/x-pack/plugins/security/server/saved_objects/ensure_authorized.test.ts +++ b/x-pack/plugins/security/server/saved_objects/ensure_authorized.test.ts @@ -6,6 +6,10 @@ */ import type { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import type { + CheckPrivilegesResponse, + CheckSavedObjectsPrivileges, +} from '@kbn/security-plugin-types-server'; import type { EnsureAuthorizedResult } from './ensure_authorized'; import { @@ -13,9 +17,7 @@ import { getEnsureAuthorizedActionResult, isAuthorizedForObjectInAllSpaces, } from './ensure_authorized'; -import type { CheckSavedObjectsPrivileges } from '../authorization'; import { Actions } from '../authorization'; -import type { CheckPrivilegesResponse } from '../authorization/types'; describe('ensureAuthorized', () => { function setupDependencies() { diff --git a/x-pack/plugins/security/server/saved_objects/ensure_authorized.ts b/x-pack/plugins/security/server/saved_objects/ensure_authorized.ts index 79e15be650773..950cba3f103ad 100644 --- a/x-pack/plugins/security/server/saved_objects/ensure_authorized.ts +++ b/x-pack/plugins/security/server/saved_objects/ensure_authorized.ts @@ -6,9 +6,11 @@ */ import type { SavedObjectsErrorHelpers } from '@kbn/core/server'; - -import type { Actions, CheckSavedObjectsPrivileges } from '../authorization'; -import type { CheckPrivilegesResponse } from '../authorization/types'; +import type { + Actions, + CheckPrivilegesResponse, + CheckSavedObjectsPrivileges, +} from '@kbn/security-plugin-types-server'; export interface EnsureAuthorizedDependencies { actions: Actions; diff --git a/x-pack/plugins/security/server/saved_objects/index.ts b/x-pack/plugins/security/server/saved_objects/index.ts index 6068ada7a3729..3fa610485a288 100644 --- a/x-pack/plugins/security/server/saved_objects/index.ts +++ b/x-pack/plugins/security/server/saved_objects/index.ts @@ -7,9 +7,9 @@ import type { CoreSetup } from '@kbn/core/server'; import { SavedObjectsClient } from '@kbn/core/server'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import { SavedObjectsSecurityExtension } from './saved_objects_security_extension'; -import type { AuditServiceSetup } from '../audit'; import type { AuthorizationServiceSetupInternal } from '../authorization'; interface SetupSavedObjectsParams { diff --git a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts index ff962c3421ce7..42fd2f8eb0d92 100644 --- a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts +++ b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts @@ -19,6 +19,10 @@ import type { AuthorizeUpdateObject, BulkResolveError, } from '@kbn/core-saved-objects-server'; +import type { + CheckPrivilegesResponse, + CheckSavedObjectsPrivileges, +} from '@kbn/security-plugin-types-server'; import { AuditAction, @@ -26,9 +30,7 @@ import { SecurityAction, } from './saved_objects_security_extension'; import { auditLoggerMock } from '../audit/mocks'; -import type { CheckSavedObjectsPrivileges } from '../authorization'; import { Actions } from '../authorization'; -import type { CheckPrivilegesResponse } from '../authorization/types'; const checkAuthorizationSpy = jest.spyOn( SavedObjectsSecurityExtension.prototype as any, diff --git a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts index 81b909da2c984..1eb934d2f2bdc 100644 --- a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts +++ b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts @@ -42,13 +42,16 @@ import type { import type { AuthorizeObject } from '@kbn/core-saved-objects-server/src/extensions/security'; import { ALL_NAMESPACES_STRING, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import type { EcsEvent } from '@kbn/ecs'; +import type { + Actions, + AuditLogger, + CheckPrivilegesResponse, + CheckSavedObjectsPrivileges, +} from '@kbn/security-plugin-types-server'; import { isAuthorizedInAllSpaces } from './authorization_utils'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; -import type { AuditLogger } from '../audit'; import { savedObjectEvent } from '../audit'; -import type { Actions, CheckSavedObjectsPrivileges } from '../authorization'; -import type { CheckPrivilegesResponse } from '../authorization/types'; interface Params { actions: Actions; diff --git a/x-pack/plugins/security/server/session_management/session.test.ts b/x-pack/plugins/security/server/session_management/session.test.ts index 691b6db78b518..b6f9197606e11 100644 --- a/x-pack/plugins/security/server/session_management/session.test.ts +++ b/x-pack/plugins/security/server/session_management/session.test.ts @@ -9,6 +9,7 @@ import nodeCrypto from '@elastic/node-crypto'; import crypto from 'crypto'; import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import type { AuditLogger } from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { sessionCookieMock, sessionIndexMock, sessionMock } from './index.mock'; @@ -21,7 +22,6 @@ import { SessionUnexpectedError, } from './session_errors'; import type { SessionIndex } from './session_index'; -import type { AuditLogger } from '..'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; import { userSessionConcurrentLimitLogoutEvent } from '../audit'; import { auditLoggerMock, auditServiceMock } from '../audit/mocks'; diff --git a/x-pack/plugins/security/server/session_management/session.ts b/x-pack/plugins/security/server/session_management/session.ts index 38b3cc7d2443c..afc917bde78be 100644 --- a/x-pack/plugins/security/server/session_management/session.ts +++ b/x-pack/plugins/security/server/session_management/session.ts @@ -11,6 +11,7 @@ import { createHash, randomBytes } from 'crypto'; import { promisify } from 'util'; import type { KibanaRequest, Logger } from '@kbn/core/server'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { SessionCookie } from './session_cookie'; @@ -21,7 +22,6 @@ import { SessionUnexpectedError, } from './session_errors'; import type { SessionIndex, SessionIndexValue } from './session_index'; -import type { AuditServiceSetup } from '..'; import type { AuthenticationProvider } from '../../common'; import { userSessionConcurrentLimitLogoutEvent } from '../audit'; import type { ConfigType } from '../config'; diff --git a/x-pack/plugins/security/server/session_management/session_index.test.ts b/x-pack/plugins/security/server/session_management/session_index.test.ts index 2a837a7be7807..e1890273469ed 100644 --- a/x-pack/plugins/security/server/session_management/session_index.test.ts +++ b/x-pack/plugins/security/server/session_management/session_index.test.ts @@ -16,6 +16,7 @@ import type { } from '@elastic/elasticsearch/lib/api/types'; import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import type { AuditLogger } from '@kbn/security-plugin-types-server'; import { getSessionIndexSettings, @@ -23,7 +24,6 @@ import { SessionIndex, } from './session_index'; import { sessionIndexMock } from './session_index.mock'; -import type { AuditLogger } from '../audit'; import { auditLoggerMock } from '../audit/mocks'; import { AnonymousAuthenticationProvider } from '../authentication'; import { ConfigSchema, createConfig } from '../config'; diff --git a/x-pack/plugins/security/server/session_management/session_index.ts b/x-pack/plugins/security/server/session_management/session_index.ts index 54f448d151389..700904d4676d7 100644 --- a/x-pack/plugins/security/server/session_management/session_index.ts +++ b/x-pack/plugins/security/server/session_management/session_index.ts @@ -20,9 +20,9 @@ import type { import semver from 'semver'; import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { AuditLogger } from '@kbn/security-plugin-types-server'; -import type { AuthenticationProvider } from '../../common/model'; -import type { AuditLogger } from '../audit'; +import type { AuthenticationProvider } from '../../common'; import { sessionCleanupConcurrentLimitEvent, sessionCleanupEvent } from '../audit'; import { AnonymousAuthenticationProvider } from '../authentication'; import type { ConfigType } from '../config'; diff --git a/x-pack/plugins/security/server/session_management/session_management_service.test.ts b/x-pack/plugins/security/server/session_management/session_management_service.test.ts index 46bb0499f8e4d..e59ba59600b29 100644 --- a/x-pack/plugins/security/server/session_management/session_management_service.test.ts +++ b/x-pack/plugins/security/server/session_management/session_management_service.test.ts @@ -8,6 +8,7 @@ import { Subject } from 'rxjs'; import { coreMock, elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { TaskManagerStartContract, TaskRunCreatorFunction, @@ -21,7 +22,6 @@ import { SESSION_INDEX_CLEANUP_TASK_NAME, SessionManagementService, } from './session_management_service'; -import type { AuditServiceSetup } from '../audit'; import { auditServiceMock } from '../audit/mocks'; import { ConfigSchema, createConfig } from '../config'; import type { OnlineStatusRetryScheduler } from '../elasticsearch'; diff --git a/x-pack/plugins/security/server/session_management/session_management_service.ts b/x-pack/plugins/security/server/session_management/session_management_service.ts index 4c3298f69bca2..448f5f060500f 100644 --- a/x-pack/plugins/security/server/session_management/session_management_service.ts +++ b/x-pack/plugins/security/server/session_management/session_management_service.ts @@ -10,6 +10,7 @@ import { switchMap } from 'rxjs'; import type { ElasticsearchClient, HttpServiceSetup, Logger } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import type { AuditServiceSetup } from '@kbn/security-plugin-types-server'; import type { TaskManagerSetupContract, TaskManagerStartContract, @@ -18,7 +19,6 @@ import type { import { Session } from './session'; import { SessionCookie } from './session_cookie'; import { SessionIndex } from './session_index'; -import type { AuditServiceSetup } from '../audit'; import type { ConfigType } from '../config'; import type { OnlineStatusRetryScheduler } from '../elasticsearch'; diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts index f990bf8095a1b..22b3492d26d72 100644 --- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts @@ -10,20 +10,21 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { httpServerMock } from '@kbn/core/server/mocks'; import { savedObjectsExtensionsMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; +import type { + AuditEvent, + AuditLogger, + AuthorizationServiceSetup, + CheckPrivilegesResponse, +} from '@kbn/security-plugin-types-server'; import type { GetAllSpacesPurpose, Space } from '@kbn/spaces-plugin/server'; import { spacesClientMock } from '@kbn/spaces-plugin/server/mocks'; import { deepFreeze } from '@kbn/std'; import { SecureSpacesClientWrapper } from './secure_spaces_client_wrapper'; -import type { AuditEvent, AuditLogger } from '../audit'; import { SpaceAuditAction } from '../audit'; import { auditLoggerMock } from '../audit/mocks'; -import type { - AuthorizationServiceSetup, - AuthorizationServiceSetupInternal, -} from '../authorization'; +import type { AuthorizationServiceSetupInternal } from '../authorization'; import { authorizationMock } from '../authorization/index.mock'; -import type { CheckPrivilegesResponse } from '../authorization/types'; interface Opts { securityEnabled?: boolean; diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts index 1e9e25b6b800d..a4f57e067c098 100644 --- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts +++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts @@ -10,6 +10,7 @@ import Boom from '@hapi/boom'; import type { KibanaRequest, SavedObjectsClient } from '@kbn/core/server'; import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; import type { ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; +import type { AuditLogger, AuthorizationServiceSetup } from '@kbn/security-plugin-types-server'; import type { GetAllSpacesOptions, GetAllSpacesPurpose, @@ -18,9 +19,7 @@ import type { Space, } from '@kbn/spaces-plugin/server'; -import type { AuditLogger } from '../audit'; import { SpaceAuditAction, spaceAuditEvent } from '../audit'; -import type { AuthorizationServiceSetup } from '../authorization'; import type { SecurityPluginSetup } from '../plugin'; const PURPOSE_PRIVILEGE_MAP: Record< diff --git a/x-pack/plugins/security/server/spaces/setup_spaces_client.ts b/x-pack/plugins/security/server/spaces/setup_spaces_client.ts index 43351c575d395..6ca68a395fbc7 100644 --- a/x-pack/plugins/security/server/spaces/setup_spaces_client.ts +++ b/x-pack/plugins/security/server/spaces/setup_spaces_client.ts @@ -6,11 +6,13 @@ */ import { SavedObjectsClient } from '@kbn/core/server'; +import type { + AuditServiceSetup, + AuthorizationServiceSetup, +} from '@kbn/security-plugin-types-server'; import type { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; import { SecureSpacesClientWrapper } from './secure_spaces_client_wrapper'; -import type { AuditServiceSetup } from '../audit'; -import type { AuthorizationServiceSetup } from '../authorization'; import { SavedObjectsSecurityExtension } from '../saved_objects'; interface Deps { diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts index d120aae57a518..19378bfd8488b 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts @@ -13,7 +13,7 @@ import { } from '@kbn/usage-collection-plugin/server/mocks'; import { registerSecurityUsageCollector } from './security_usage_collector'; -import type { SecurityLicenseFeatures } from '../../common/licensing'; +import type { SecurityLicenseFeatures } from '../../common'; import { licenseMock } from '../../common/licensing/index.mock'; import { ConfigSchema, createConfig } from '../config'; diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts index 5859943391442..fc761fb13a50e 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts @@ -7,7 +7,7 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; -import type { SecurityLicense } from '../../common/licensing'; +import type { SecurityLicense } from '../../common'; import type { ConfigType } from '../config'; interface Usage { diff --git a/x-pack/plugins/security/server/user_profile/index.ts b/x-pack/plugins/security/server/user_profile/index.ts index 9c6fd67458e6b..a324c685bf7fb 100644 --- a/x-pack/plugins/security/server/user_profile/index.ts +++ b/x-pack/plugins/security/server/user_profile/index.ts @@ -8,12 +8,7 @@ export { UserProfileService } from './user_profile_service'; export type { - UserProfileServiceStart, UserProfileServiceStartInternal, UserProfileServiceStartParams, - UserProfileSuggestParams, - UserProfileBulkGetParams, - UserProfileRequiredPrivileges, - UserProfileGetCurrentParams, } from './user_profile_service'; export type { UserProfileGrant } from './user_profile_grant'; diff --git a/x-pack/plugins/security/server/user_profile/user_profile_service.ts b/x-pack/plugins/security/server/user_profile/user_profile_service.ts index 8ef87b60f9e15..b6e0912b804dc 100644 --- a/x-pack/plugins/security/server/user_profile/user_profile_service.ts +++ b/x-pack/plugins/security/server/user_profile/user_profile_service.ts @@ -8,7 +8,15 @@ import type { SecurityActivateUserProfileRequest } from '@elastic/elasticsearch/lib/api/types'; import type { SecurityUserProfile } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { IClusterClient, KibanaRequest, Logger } from '@kbn/core/server'; +import type { IClusterClient, Logger } from '@kbn/core/server'; +import type { + CheckUserProfilesPrivilegesResponse, + UserProfileBulkGetParams, + UserProfileGetCurrentParams, + UserProfileRequiredPrivileges, + UserProfileServiceStart, + UserProfileSuggestParams, +} from '@kbn/security-plugin-types-server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { UserProfileGrant } from './user_profile_grant'; @@ -16,11 +24,9 @@ import type { SecurityLicense, UserProfile, UserProfileData, - UserProfileLabels, UserProfileWithSecurity, } from '../../common'; import type { AuthorizationServiceSetupInternal } from '../authorization'; -import type { CheckUserProfilesPrivilegesResponse } from '../authorization/types'; import { getDetailedErrorMessage, getErrorStatusCode } from '../errors'; import { getPrintableSessionId, type Session } from '../session_management'; @@ -31,46 +37,6 @@ const MAX_SUGGESTIONS_COUNT = 100; const DEFAULT_SUGGESTIONS_COUNT = 10; const MIN_SUGGESTIONS_FOR_PRIVILEGES_CHECK = 10; -/** - * A set of methods to work with Kibana user profiles. - */ -export interface UserProfileServiceStart { - /** - * Retrieves a user profile for the current user extracted from the specified request. If the profile isn't available, - * e.g. for the anonymous users or users authenticated via authenticating proxies, the `null` value is returned. - * @param params Get current user profile operation parameters. - * @param params.request User request instance to get user profile for. - * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The - * optional "dataPath" parameter can be used to return personal data for the requested user profiles. - */ - getCurrent( - params: UserProfileGetCurrentParams - ): Promise | null>; - - /** - * Retrieves multiple user profiles by their identifiers. - * @param params Bulk get operation parameters. - * @param params.uids List of user profile identifiers. - * @param params.dataPath By default Elasticsearch returns user information, but does not return any user data. The - * optional "dataPath" parameter can be used to return personal data for the requested user profiles. - */ - bulkGet( - params: UserProfileBulkGetParams - ): Promise>>; - - /** - * Suggests multiple user profiles by search criteria. - * @param params Suggest operation parameters. - * @param params.name Query string used to match name-related fields in user profiles. The following fields are treated as name-related: username, full_name and email. - * @param params.size Desired number of suggestion to return. The default value is 10. - * @param params.dataPath By default, suggest API returns user information, but does not return any user data. The optional "dataPath" parameter can be used to return personal data for this user (within `kibana` namespace only). - * @param params.requiredPrivileges The set of the privileges that users associated with the suggested user profile should have in the specified space. If not specified, privileges check isn't performed and all matched profiles are returned irrespective to the privileges of the associated users. - */ - suggest( - params: UserProfileSuggestParams - ): Promise>>; -} - export interface UserProfileServiceStartInternal extends UserProfileServiceStart { /** * Activates user profile using provided user profile grant. @@ -96,94 +62,6 @@ export interface UserProfileServiceStartParams { session: PublicMethodsOf; } -/** - * The set of privileges that users associated with the suggested user profile should have for a specified space id. - */ -export interface UserProfileRequiredPrivileges { - /** - * The id of the Kibana Space. - */ - spaceId: string; - - /** - * The set of the Kibana specific application privileges. - */ - privileges: { kibana: string[] }; -} - -/** - * Parameters for the get user profile for the current user API. - */ -export interface UserProfileGetCurrentParams { - /** - * User request instance to get user profile for. - */ - request: KibanaRequest; - - /** - * By default, get API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath?: string; -} - -/** - * Parameters for the bulk get API. - */ -export interface UserProfileBulkGetParams { - /** - * List of user profile identifiers. - */ - uids: Set; - - /** - * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath?: string; -} - -/** - * Parameters for the suggest API. - */ -export interface UserProfileSuggestParams { - /** - * Query string used to match name-related fields in user profiles. The following fields are treated as - * name-related: username, full_name and email. - */ - name?: string; - - /** - * Extra search criteria to improve relevance of the suggestion result. A profile matching the - * specified hint is ranked higher in the response. But not-matching the hint does not exclude a - * profile from the response as long as it matches the `name` field query. - */ - hint?: { - /** - * A list of Profile UIDs to match against. - */ - uids: string[]; - }; - - /** - * Desired number of suggestion to return. The default value is 10. - */ - size?: number; - - /** - * By default, suggest API returns user information, but does not return any user data. The optional "dataPath" - * parameter can be used to return personal data for this user (within `kibana` namespace only). - */ - dataPath?: string; - - /** - * The set of the privileges that users associated with the suggested user profile should have in the specified space. - * If not specified, privileges check isn't performed and all matched profiles are returned irrespective to the - * privileges of the associated users. - */ - requiredPrivileges?: UserProfileRequiredPrivileges; -} - function parseUserProfile( rawUserProfile: SecurityUserProfile ): UserProfile { diff --git a/x-pack/plugins/security/server/user_profile/user_setting_service.ts b/x-pack/plugins/security/server/user_profile/user_setting_service.ts index 7c5ca3c1c7ef8..f423d75e1a041 100644 --- a/x-pack/plugins/security/server/user_profile/user_setting_service.ts +++ b/x-pack/plugins/security/server/user_profile/user_setting_service.ts @@ -7,8 +7,10 @@ import type { KibanaRequest } from '@kbn/core-http-server'; import type { Logger } from '@kbn/logging'; - -import type { UserProfileGetCurrentParams, UserProfileServiceStart } from './user_profile_service'; +import type { + UserProfileGetCurrentParams, + UserProfileServiceStart, +} from '@kbn/security-plugin-types-server'; export interface UserSettingServiceStart { /** diff --git a/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts b/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts index 07e30826d8f0c..d27887d5716f9 100644 --- a/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts +++ b/x-pack/plugins/security/server/user_profile/user_settings_service.test.ts @@ -12,8 +12,8 @@ import { httpServerMock, loggingSystemMock, } from '@kbn/core/server/mocks'; +import type { UserProfileServiceStart } from '@kbn/security-plugin-types-server'; -import type { UserProfileServiceStart } from './user_profile_service'; import { UserProfileService } from './user_profile_service'; import { UserSettingService } from './user_setting_service'; import type { UserProfileWithSecurity } from '../../common'; diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 2a055a61bbad5..3a0f7062ec205 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -63,6 +63,9 @@ "@kbn/core-user-settings-server", "@kbn/remote-clusters-plugin", "@kbn/analytics-client", + "@kbn/security-plugin-types-common", + "@kbn/security-plugin-types-public", + "@kbn/security-plugin-types-server" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/common/api/asset_criticality/status.yaml b/x-pack/plugins/security_solution/common/api/asset_criticality/status.yaml new file mode 100644 index 0000000000000..7bdc5fd562c36 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/asset_criticality/status.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Asset Criticality Status Schema +paths: + /internal/asset_criticality/status: + get: + summary: Get Asset Criticality Status + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/AssetCriticalityStatusResponse' + '400': + description: Invalid request + responses: + +components: + schemas: + AssetCriticalityStatusResponse: + type: object + properties: + asset_criticality_resources_installed: + type: boolean \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts index 10901049d476d..eebcde55a0301 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts @@ -6,7 +6,6 @@ */ import { z } from 'zod'; -import { requiredOptional } from '@kbn/zod-helpers'; /* * NOTICE: Do not edit this file manually. @@ -27,48 +26,42 @@ export const EcsMapping = z.object({}).catchall( ); export type OsqueryQuery = z.infer; -export const OsqueryQuery = z - .object({ - /** - * Query ID - */ - id: z.string(), - /** - * Query to execute - */ - query: z.string(), - ecs_mapping: EcsMapping.optional(), - /** - * Query version - */ - version: z.string().optional(), - platform: z.string().optional(), - removed: z.boolean().optional(), - snapshot: z.boolean().optional(), - }) - .transform(requiredOptional); +export const OsqueryQuery = z.object({ + /** + * Query ID + */ + id: z.string(), + /** + * Query to execute + */ + query: z.string(), + ecs_mapping: EcsMapping.optional(), + /** + * Query version + */ + version: z.string().optional(), + platform: z.string().optional(), + removed: z.boolean().optional(), + snapshot: z.boolean().optional(), +}); export type OsqueryParams = z.infer; -export const OsqueryParams = z - .object({ - query: z.string().optional(), - ecs_mapping: EcsMapping.optional(), - queries: z.array(OsqueryQuery).optional(), - pack_id: z.string().optional(), - saved_query_id: z.string().optional(), - }) - .transform(requiredOptional); +export const OsqueryParams = z.object({ + query: z.string().optional(), + ecs_mapping: EcsMapping.optional(), + queries: z.array(OsqueryQuery).optional(), + pack_id: z.string().optional(), + saved_query_id: z.string().optional(), +}); export type OsqueryParamsCamelCase = z.infer; -export const OsqueryParamsCamelCase = z - .object({ - query: z.string().optional(), - ecsMapping: EcsMapping.optional(), - queries: z.array(OsqueryQuery).optional(), - packId: z.string().optional(), - savedQueryId: z.string().optional(), - }) - .transform(requiredOptional); +export const OsqueryParamsCamelCase = z.object({ + query: z.string().optional(), + ecsMapping: EcsMapping.optional(), + queries: z.array(OsqueryQuery).optional(), + packId: z.string().optional(), + savedQueryId: z.string().optional(), +}); export type OsqueryResponseAction = z.infer; export const OsqueryResponseAction = z.object({ @@ -83,12 +76,10 @@ export const RuleResponseOsqueryAction = z.object({ }); export type EndpointParams = z.infer; -export const EndpointParams = z - .object({ - command: z.literal('isolate'), - comment: z.string().optional(), - }) - .transform(requiredOptional); +export const EndpointParams = z.object({ + command: z.literal('isolate'), + comment: z.string().optional(), +}); export type EndpointResponseAction = z.infer; export const EndpointResponseAction = z.object({ diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml index 6cc6f0c46465c..9d27bb6e4a97c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml @@ -49,7 +49,6 @@ components: required: - id - query - x-modify: requiredOptional OsqueryParams: type: object @@ -66,7 +65,6 @@ components: type: string saved_query_id: type: string - x-modify: requiredOptional OsqueryParamsCamelCase: type: object @@ -83,7 +81,6 @@ components: type: string savedQueryId: type: string - x-modify: requiredOptional OsqueryResponseAction: type: object @@ -123,7 +120,6 @@ components: type: string required: - command - x-modify: requiredOptional EndpointResponseAction: type: object diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index 79ad21ddfb009..4b9001dc35c00 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -6,7 +6,7 @@ */ import { z } from 'zod'; -import { requiredOptional, isValidDateMath } from '@kbn/zod-helpers'; +import { isValidDateMath } from '@kbn/zod-helpers'; /* * NOTICE: Do not edit this file manually. @@ -94,14 +94,12 @@ export const RiskScore = z.number().int().min(0).max(100); */ export type RiskScoreMapping = z.infer; export const RiskScoreMapping = z.array( - z - .object({ - field: z.string(), - operator: z.literal('equals'), - value: z.string(), - risk_score: RiskScore.optional(), - }) - .transform(requiredOptional) + z.object({ + field: z.string(), + operator: z.literal('equals'), + value: z.string(), + risk_score: RiskScore.optional(), + }) ); /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index ad2bfaf76c4c0..047394c5843c7 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -95,7 +95,6 @@ components: - field - operator - value - x-modify: requiredOptional description: Overrides generated alerts' risk_score with a value from the source event Severity: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts index 062c913354404..3dfc4a294965f 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts @@ -25,8 +25,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 52 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -48,8 +48,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 52 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -61,8 +61,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -75,8 +75,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -90,8 +90,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -106,8 +106,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", and 36 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -123,8 +123,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 28 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -141,9 +141,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => { @@ -160,9 +158,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => { @@ -180,9 +176,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, query, index, interval] does validate', () => { @@ -222,9 +216,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", risk_score: Required, risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score] does validate', () => { @@ -390,8 +382,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"references.0: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", references.0: Expected string, received number, references.0: Expected string, received number, and 22 more"` + expect(stringifyZodError(result.error)).toEqual( + 'references.0: Expected string, received number' ); }); @@ -403,9 +395,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` - ); + expect(stringifyZodError(result.error)).toEqual('index.0: Expected string, received number'); }); test('saved_query type can have filters with it', () => { @@ -427,9 +417,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` - ); + expect(stringifyZodError(result.error)).toEqual('filters: Expected array, received string'); }); test('language validates with kuery', () => { @@ -462,8 +450,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + expect(stringifyZodError(result.error)).toEqual( + "language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'" ); }); @@ -523,8 +511,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"tags.0: Expected string, received number, tags.1: Expected string, received number, tags.2: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", and 38 more"` + expect(stringifyZodError(result.error)).toEqual( + 'tags.0: Expected string, received number, tags.1: Expected string, received number, tags.2: Expected string, received number' ); }); @@ -551,9 +539,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"threat.0.framework: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", threat.0.framework: Required, threat.0.framework: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threat.0.framework: Required'); }); test('You cannot send in an array of threat that are missing "tactic"', () => { @@ -575,9 +561,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"threat.0.tactic: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", threat.0.tactic: Required, threat.0.tactic: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threat.0.tactic: Required'); }); test('You can send in an array of threat that are missing "technique"', () => { @@ -619,8 +603,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"false_positives.0: Expected string, received number, false_positives.1: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", false_positives.0: Expected string, received number, and 30 more"` + expect(stringifyZodError(result.error)).toEqual( + 'false_positives.0: Expected string, received number, false_positives.1: Expected string, received number' ); }); @@ -693,9 +677,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"meta: Expected object, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", meta: Expected object, received string, meta: Expected object, received string, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('meta: Expected object, received string'); }); test('You can omit the query string when filters are present', () => { @@ -730,8 +712,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', and 22 more"` + expect(stringifyZodError(result.error)).toEqual( + "severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk'" ); }); @@ -743,9 +725,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.group: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.group: Required, actions.0.group: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.group: Required'); }); test('You cannot send in an array of actions that are missing "id"', () => { @@ -756,9 +736,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.id: Required, actions.0.id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.id: Required'); }); test('You cannot send in an array of actions that are missing "action_type_id"', () => { @@ -769,9 +747,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.action_type_id: Required, actions.0.action_type_id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.action_type_id: Required'); }); test('You cannot send in an array of actions that are missing "params"', () => { @@ -782,9 +758,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.params: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.params: Required, actions.0.params: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.params: Required'); }); test('You cannot send in an array of actions that are including "actionTypeId"', () => { @@ -802,9 +776,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.action_type_id: Required, actions.0.action_type_id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.action_type_id: Required'); }); describe('note', () => { @@ -840,9 +812,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"note: Expected string, received object, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", note: Expected string, received object, note: Expected string, received object, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('note: Expected string, received object'); }); test('empty name is not valid', () => { @@ -926,9 +896,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", saved_id: Required, type: Invalid literal value, expected \\"threshold\\", and 14 more"` - ); + expect(stringifyZodError(result.error)).toEqual('saved_id: Required'); }); test('threshold is required when type is threshold and will not validate without it', () => { @@ -936,9 +904,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 14 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threshold: Required'); }); test('threshold rules fail validation if threshold is not greater than 0', () => { @@ -1016,8 +982,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"exceptions_list.0.list_id: Required, exceptions_list.0.type: Required, exceptions_list.0.namespace_type: Invalid enum value. Expected 'agnostic' | 'single', received 'not a namespace type', type: Invalid literal value, expected \\"eql\\", query: Required, and 43 more"` + expect(stringifyZodError(result.error)).toEqual( + "exceptions_list.0.list_id: Required, exceptions_list.0.type: Required, exceptions_list.0.namespace_type: Invalid enum value. Expected 'agnostic' | 'single', received 'not a namespace type'" ); }); @@ -1059,8 +1025,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 14 more"` + expect(stringifyZodError(result.error)).toEqual( + 'threat_query: Required, threat_mapping: Required, threat_index: Required' ); }); @@ -1130,8 +1096,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", data_view_id: Expected string, received number, data_view_id: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + expect(stringifyZodError(result.error)).toEqual( + 'data_view_id: Expected string, received number' ); }); @@ -1195,9 +1161,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields.field_names: Required, investigation_fields.field_names: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('investigation_fields.field_names: Required'); }); test('You can send in investigation_fields', () => { @@ -1232,8 +1196,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names.0: Expected string, received number, investigation_fields.field_names.1: Expected string, received number, investigation_fields.field_names.2: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", and 38 more"` + expect(stringifyZodError(result.error)).toEqual( + 'investigation_fields.field_names.0: Expected string, received number, investigation_fields.field_names.1: Expected string, received number, investigation_fields.field_names.2: Expected string, received number' ); }); @@ -1245,9 +1209,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields.field_names: Required, investigation_fields.field_names: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('investigation_fields.field_names: Required'); }); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts index d1432e5a67352..e05aa65c4fa0d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts @@ -41,7 +41,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 15 more"` + `"type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -70,9 +70,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", saved_id: Required, type: Invalid literal value, expected \\"threshold\\", and 14 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"saved_id: Required"`); }); test('it should validate a type of "timeline_id" if there is a "timeline_title" dependent', () => { @@ -103,7 +101,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"exceptions_list: Expected array, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", exceptions_list: Expected array, received string, exceptions_list: Expected array, received string, and 22 more"` + `"exceptions_list: Expected array, received string"` ); }); }); @@ -239,7 +237,7 @@ describe('investigation_fields', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields: Expected object, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields: Expected object, received string, investigation_fields: Expected object, received string, and 22 more"` + `"investigation_fields: Expected object, received string"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts index ff99655a75e89..1051f59feac0a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts @@ -131,20 +131,20 @@ export const BaseDefaultableFields = z.object({ export type BaseCreateProps = z.infer; export const BaseCreateProps = - BaseRequiredFields.and(BaseOptionalFields).and(BaseDefaultableFields); + BaseRequiredFields.merge(BaseOptionalFields).merge(BaseDefaultableFields); export type BasePatchProps = z.infer; export const BasePatchProps = BaseRequiredFields.partial() - .and(BaseOptionalFields) - .and(BaseDefaultableFields); + .merge(BaseOptionalFields) + .merge(BaseDefaultableFields); export type BaseResponseProps = z.infer; -export const BaseResponseProps = BaseRequiredFields.and(BaseOptionalFields).and( +export const BaseResponseProps = BaseRequiredFields.merge(BaseOptionalFields).merge( BaseDefaultableFields.required() ); -export type ResponseRequiredFields = z.infer; -export const ResponseRequiredFields = z.object({ +export type ResponseFields = z.infer; +export const ResponseFields = z.object({ id: RuleObjectId, rule_id: RuleSignatureId, immutable: IsRuleImmutable, @@ -156,22 +156,18 @@ export const ResponseRequiredFields = z.object({ related_integrations: RelatedIntegrationArray, required_fields: RequiredFieldArray, setup: SetupGuide, -}); - -export type ResponseOptionalFields = z.infer; -export const ResponseOptionalFields = z.object({ execution_summary: RuleExecutionSummary.optional(), }); export type SharedCreateProps = z.infer; -export const SharedCreateProps = BaseCreateProps.and( +export const SharedCreateProps = BaseCreateProps.merge( z.object({ rule_id: RuleSignatureId.optional(), }) ); export type SharedUpdateProps = z.infer; -export const SharedUpdateProps = BaseCreateProps.and( +export const SharedUpdateProps = BaseCreateProps.merge( z.object({ id: RuleObjectId.optional(), rule_id: RuleSignatureId.optional(), @@ -179,7 +175,7 @@ export const SharedUpdateProps = BaseCreateProps.and( ); export type SharedPatchProps = z.infer; -export const SharedPatchProps = BasePatchProps.and( +export const SharedPatchProps = BasePatchProps.merge( z.object({ id: RuleObjectId.optional(), rule_id: RuleSignatureId.optional(), @@ -187,8 +183,7 @@ export const SharedPatchProps = BasePatchProps.and( ); export type SharedResponseProps = z.infer; -export const SharedResponseProps = - BaseResponseProps.and(ResponseRequiredFields).and(ResponseOptionalFields); +export const SharedResponseProps = BaseResponseProps.merge(ResponseFields); export type EqlQueryLanguage = z.infer; export const EqlQueryLanguage = z.literal('eql'); @@ -220,25 +215,25 @@ export const EqlOptionalFields = z.object({ }); export type EqlRuleCreateFields = z.infer; -export const EqlRuleCreateFields = EqlRequiredFields.and(EqlOptionalFields); +export const EqlRuleCreateFields = EqlRequiredFields.merge(EqlOptionalFields); export type EqlRuleResponseFields = z.infer; -export const EqlRuleResponseFields = EqlRequiredFields.and(EqlOptionalFields); +export const EqlRuleResponseFields = EqlRequiredFields.merge(EqlOptionalFields); export type EqlRulePatchFields = z.infer; -export const EqlRulePatchFields = EqlRequiredFields.partial().and(EqlOptionalFields); +export const EqlRulePatchFields = EqlRequiredFields.partial().merge(EqlOptionalFields); export type EqlRule = z.infer; -export const EqlRule = SharedResponseProps.and(EqlRuleResponseFields); +export const EqlRule = SharedResponseProps.merge(EqlRuleResponseFields); export type EqlRuleCreateProps = z.infer; -export const EqlRuleCreateProps = SharedCreateProps.and(EqlRuleCreateFields); +export const EqlRuleCreateProps = SharedCreateProps.merge(EqlRuleCreateFields); export type EqlRuleUpdateProps = z.infer; -export const EqlRuleUpdateProps = SharedUpdateProps.and(EqlRuleCreateFields); +export const EqlRuleUpdateProps = SharedUpdateProps.merge(EqlRuleCreateFields); export type EqlRulePatchProps = z.infer; -export const EqlRulePatchProps = SharedPatchProps.and(EqlRulePatchFields); +export const EqlRulePatchProps = SharedPatchProps.merge(EqlRulePatchFields); export type QueryRuleRequiredFields = z.infer; export const QueryRuleRequiredFields = z.object({ @@ -265,31 +260,31 @@ export const QueryRuleDefaultableFields = z.object({ }); export type QueryRuleCreateFields = z.infer; -export const QueryRuleCreateFields = QueryRuleRequiredFields.and(QueryRuleOptionalFields).and( +export const QueryRuleCreateFields = QueryRuleRequiredFields.merge(QueryRuleOptionalFields).merge( QueryRuleDefaultableFields ); export type QueryRulePatchFields = z.infer; export const QueryRulePatchFields = QueryRuleRequiredFields.partial() - .and(QueryRuleOptionalFields) - .and(QueryRuleDefaultableFields); + .merge(QueryRuleOptionalFields) + .merge(QueryRuleDefaultableFields); export type QueryRuleResponseFields = z.infer; -export const QueryRuleResponseFields = QueryRuleRequiredFields.and(QueryRuleOptionalFields).and( +export const QueryRuleResponseFields = QueryRuleRequiredFields.merge(QueryRuleOptionalFields).merge( QueryRuleDefaultableFields.required() ); export type QueryRule = z.infer; -export const QueryRule = SharedResponseProps.and(QueryRuleResponseFields); +export const QueryRule = SharedResponseProps.merge(QueryRuleResponseFields); export type QueryRuleCreateProps = z.infer; -export const QueryRuleCreateProps = SharedCreateProps.and(QueryRuleCreateFields); +export const QueryRuleCreateProps = SharedCreateProps.merge(QueryRuleCreateFields); export type QueryRuleUpdateProps = z.infer; -export const QueryRuleUpdateProps = SharedUpdateProps.and(QueryRuleCreateFields); +export const QueryRuleUpdateProps = SharedUpdateProps.merge(QueryRuleCreateFields); export type QueryRulePatchProps = z.infer; -export const QueryRulePatchProps = SharedPatchProps.and(QueryRulePatchFields); +export const QueryRulePatchProps = SharedPatchProps.merge(QueryRulePatchFields); export type SavedQueryRuleRequiredFields = z.infer; export const SavedQueryRuleRequiredFields = z.object({ @@ -316,31 +311,31 @@ export const SavedQueryRuleDefaultableFields = z.object({ }); export type SavedQueryRuleCreateFields = z.infer; -export const SavedQueryRuleCreateFields = SavedQueryRuleRequiredFields.and( +export const SavedQueryRuleCreateFields = SavedQueryRuleRequiredFields.merge( SavedQueryRuleOptionalFields -).and(SavedQueryRuleDefaultableFields); +).merge(SavedQueryRuleDefaultableFields); export type SavedQueryRulePatchFields = z.infer; export const SavedQueryRulePatchFields = SavedQueryRuleRequiredFields.partial() - .and(SavedQueryRuleOptionalFields) - .and(SavedQueryRuleDefaultableFields); + .merge(SavedQueryRuleOptionalFields) + .merge(SavedQueryRuleDefaultableFields); export type SavedQueryRuleResponseFields = z.infer; -export const SavedQueryRuleResponseFields = SavedQueryRuleRequiredFields.and( +export const SavedQueryRuleResponseFields = SavedQueryRuleRequiredFields.merge( SavedQueryRuleOptionalFields -).and(SavedQueryRuleDefaultableFields.required()); +).merge(SavedQueryRuleDefaultableFields.required()); export type SavedQueryRule = z.infer; -export const SavedQueryRule = SharedResponseProps.and(SavedQueryRuleResponseFields); +export const SavedQueryRule = SharedResponseProps.merge(SavedQueryRuleResponseFields); export type SavedQueryRuleCreateProps = z.infer; -export const SavedQueryRuleCreateProps = SharedCreateProps.and(SavedQueryRuleCreateFields); +export const SavedQueryRuleCreateProps = SharedCreateProps.merge(SavedQueryRuleCreateFields); export type SavedQueryRuleUpdateProps = z.infer; -export const SavedQueryRuleUpdateProps = SharedUpdateProps.and(SavedQueryRuleCreateFields); +export const SavedQueryRuleUpdateProps = SharedUpdateProps.merge(SavedQueryRuleCreateFields); export type SavedQueryRulePatchProps = z.infer; -export const SavedQueryRulePatchProps = SharedPatchProps.and(SavedQueryRulePatchFields); +export const SavedQueryRulePatchProps = SharedPatchProps.merge(SavedQueryRulePatchFields); export type ThresholdRuleRequiredFields = z.infer; export const ThresholdRuleRequiredFields = z.object({ @@ -366,31 +361,31 @@ export const ThresholdRuleDefaultableFields = z.object({ }); export type ThresholdRuleCreateFields = z.infer; -export const ThresholdRuleCreateFields = ThresholdRuleRequiredFields.and( +export const ThresholdRuleCreateFields = ThresholdRuleRequiredFields.merge( ThresholdRuleOptionalFields -).and(ThresholdRuleDefaultableFields); +).merge(ThresholdRuleDefaultableFields); export type ThresholdRulePatchFields = z.infer; export const ThresholdRulePatchFields = ThresholdRuleRequiredFields.partial() - .and(ThresholdRuleOptionalFields) - .and(ThresholdRuleDefaultableFields); + .merge(ThresholdRuleOptionalFields) + .merge(ThresholdRuleDefaultableFields); export type ThresholdRuleResponseFields = z.infer; -export const ThresholdRuleResponseFields = ThresholdRuleRequiredFields.and( +export const ThresholdRuleResponseFields = ThresholdRuleRequiredFields.merge( ThresholdRuleOptionalFields -).and(ThresholdRuleDefaultableFields.required()); +).merge(ThresholdRuleDefaultableFields.required()); export type ThresholdRule = z.infer; -export const ThresholdRule = SharedResponseProps.and(ThresholdRuleResponseFields); +export const ThresholdRule = SharedResponseProps.merge(ThresholdRuleResponseFields); export type ThresholdRuleCreateProps = z.infer; -export const ThresholdRuleCreateProps = SharedCreateProps.and(ThresholdRuleCreateFields); +export const ThresholdRuleCreateProps = SharedCreateProps.merge(ThresholdRuleCreateFields); export type ThresholdRuleUpdateProps = z.infer; -export const ThresholdRuleUpdateProps = SharedUpdateProps.and(ThresholdRuleCreateFields); +export const ThresholdRuleUpdateProps = SharedUpdateProps.merge(ThresholdRuleCreateFields); export type ThresholdRulePatchProps = z.infer; -export const ThresholdRulePatchProps = SharedPatchProps.and(ThresholdRulePatchFields); +export const ThresholdRulePatchProps = SharedPatchProps.merge(ThresholdRulePatchFields); export type ThreatMatchRuleRequiredFields = z.infer; export const ThreatMatchRuleRequiredFields = z.object({ @@ -423,31 +418,31 @@ export const ThreatMatchRuleDefaultableFields = z.object({ }); export type ThreatMatchRuleCreateFields = z.infer; -export const ThreatMatchRuleCreateFields = ThreatMatchRuleRequiredFields.and( +export const ThreatMatchRuleCreateFields = ThreatMatchRuleRequiredFields.merge( ThreatMatchRuleOptionalFields -).and(ThreatMatchRuleDefaultableFields); +).merge(ThreatMatchRuleDefaultableFields); export type ThreatMatchRulePatchFields = z.infer; export const ThreatMatchRulePatchFields = ThreatMatchRuleRequiredFields.partial() - .and(ThreatMatchRuleOptionalFields) - .and(ThreatMatchRuleDefaultableFields); + .merge(ThreatMatchRuleOptionalFields) + .merge(ThreatMatchRuleDefaultableFields); export type ThreatMatchRuleResponseFields = z.infer; -export const ThreatMatchRuleResponseFields = ThreatMatchRuleRequiredFields.and( +export const ThreatMatchRuleResponseFields = ThreatMatchRuleRequiredFields.merge( ThreatMatchRuleOptionalFields -).and(ThreatMatchRuleDefaultableFields.required()); +).merge(ThreatMatchRuleDefaultableFields.required()); export type ThreatMatchRule = z.infer; -export const ThreatMatchRule = SharedResponseProps.and(ThreatMatchRuleResponseFields); +export const ThreatMatchRule = SharedResponseProps.merge(ThreatMatchRuleResponseFields); export type ThreatMatchRuleCreateProps = z.infer; -export const ThreatMatchRuleCreateProps = SharedCreateProps.and(ThreatMatchRuleCreateFields); +export const ThreatMatchRuleCreateProps = SharedCreateProps.merge(ThreatMatchRuleCreateFields); export type ThreatMatchRuleUpdateProps = z.infer; -export const ThreatMatchRuleUpdateProps = SharedUpdateProps.and(ThreatMatchRuleCreateFields); +export const ThreatMatchRuleUpdateProps = SharedUpdateProps.merge(ThreatMatchRuleCreateFields); export type ThreatMatchRulePatchProps = z.infer; -export const ThreatMatchRulePatchProps = SharedPatchProps.and(ThreatMatchRulePatchFields); +export const ThreatMatchRulePatchProps = SharedPatchProps.merge(ThreatMatchRulePatchFields); export type MachineLearningRuleRequiredFields = z.infer; export const MachineLearningRuleRequiredFields = z.object({ @@ -469,20 +464,20 @@ export type MachineLearningRuleCreateFields = z.infer; -export const MachineLearningRule = SharedResponseProps.and(MachineLearningRuleResponseFields); +export const MachineLearningRule = SharedResponseProps.merge(MachineLearningRuleResponseFields); export type MachineLearningRuleCreateProps = z.infer; -export const MachineLearningRuleCreateProps = SharedCreateProps.and( +export const MachineLearningRuleCreateProps = SharedCreateProps.merge( MachineLearningRuleCreateFields ); export type MachineLearningRuleUpdateProps = z.infer; -export const MachineLearningRuleUpdateProps = SharedUpdateProps.and( +export const MachineLearningRuleUpdateProps = SharedUpdateProps.merge( MachineLearningRuleCreateFields ); export type MachineLearningRulePatchProps = z.infer; -export const MachineLearningRulePatchProps = SharedPatchProps.and(MachineLearningRulePatchFields); +export const MachineLearningRulePatchProps = SharedPatchProps.merge(MachineLearningRulePatchFields); export type NewTermsRuleRequiredFields = z.infer; export const NewTermsRuleRequiredFields = z.object({ @@ -509,30 +504,30 @@ export const NewTermsRuleDefaultableFields = z.object({ export type NewTermsRulePatchFields = z.infer; export const NewTermsRulePatchFields = NewTermsRuleRequiredFields.partial() - .and(NewTermsRuleOptionalFields) - .and(NewTermsRuleDefaultableFields); + .merge(NewTermsRuleOptionalFields) + .merge(NewTermsRuleDefaultableFields); export type NewTermsRuleResponseFields = z.infer; -export const NewTermsRuleResponseFields = NewTermsRuleRequiredFields.and( +export const NewTermsRuleResponseFields = NewTermsRuleRequiredFields.merge( NewTermsRuleOptionalFields -).and(NewTermsRuleDefaultableFields.required()); +).merge(NewTermsRuleDefaultableFields.required()); export type NewTermsRuleCreateFields = z.infer; -export const NewTermsRuleCreateFields = NewTermsRuleRequiredFields.and( +export const NewTermsRuleCreateFields = NewTermsRuleRequiredFields.merge( NewTermsRuleOptionalFields -).and(NewTermsRuleDefaultableFields); +).merge(NewTermsRuleDefaultableFields); export type NewTermsRule = z.infer; -export const NewTermsRule = SharedResponseProps.and(NewTermsRuleResponseFields); +export const NewTermsRule = SharedResponseProps.merge(NewTermsRuleResponseFields); export type NewTermsRuleCreateProps = z.infer; -export const NewTermsRuleCreateProps = SharedCreateProps.and(NewTermsRuleCreateFields); +export const NewTermsRuleCreateProps = SharedCreateProps.merge(NewTermsRuleCreateFields); export type NewTermsRuleUpdateProps = z.infer; -export const NewTermsRuleUpdateProps = SharedUpdateProps.and(NewTermsRuleCreateFields); +export const NewTermsRuleUpdateProps = SharedUpdateProps.merge(NewTermsRuleCreateFields); export type NewTermsRulePatchProps = z.infer; -export const NewTermsRulePatchProps = SharedPatchProps.and(NewTermsRulePatchFields); +export const NewTermsRulePatchProps = SharedPatchProps.merge(NewTermsRulePatchFields); export type EsqlQueryLanguage = z.infer; export const EsqlQueryLanguage = z.literal('esql'); @@ -560,19 +555,19 @@ export type EsqlRuleCreateFields = z.infer; export const EsqlRuleCreateFields = EsqlRuleRequiredFields; export type EsqlRule = z.infer; -export const EsqlRule = SharedResponseProps.and(EsqlRuleResponseFields); +export const EsqlRule = SharedResponseProps.merge(EsqlRuleResponseFields); export type EsqlRuleCreateProps = z.infer; -export const EsqlRuleCreateProps = SharedCreateProps.and(EsqlRuleCreateFields); +export const EsqlRuleCreateProps = SharedCreateProps.merge(EsqlRuleCreateFields); export type EsqlRuleUpdateProps = z.infer; -export const EsqlRuleUpdateProps = SharedUpdateProps.and(EsqlRuleCreateFields); +export const EsqlRuleUpdateProps = SharedUpdateProps.merge(EsqlRuleCreateFields); export type EsqlRulePatchProps = z.infer; -export const EsqlRulePatchProps = SharedPatchProps.and(EsqlRulePatchFields.partial()); +export const EsqlRulePatchProps = SharedPatchProps.merge(EsqlRulePatchFields.partial()); export type TypeSpecificCreateProps = z.infer; -export const TypeSpecificCreateProps = z.union([ +export const TypeSpecificCreateProps = z.discriminatedUnion('type', [ EqlRuleCreateFields, QueryRuleCreateFields, SavedQueryRuleCreateFields, @@ -596,7 +591,7 @@ export const TypeSpecificPatchProps = z.union([ ]); export type TypeSpecificResponse = z.infer; -export const TypeSpecificResponse = z.union([ +export const TypeSpecificResponse = z.discriminatedUnion('type', [ EqlRuleResponseFields, QueryRuleResponseFields, SavedQueryRuleResponseFields, @@ -608,7 +603,7 @@ export const TypeSpecificResponse = z.union([ ]); export type RuleCreateProps = z.infer; -export const RuleCreateProps = z.union([ +export const RuleCreateProps = z.discriminatedUnion('type', [ EqlRuleCreateProps, QueryRuleCreateProps, SavedQueryRuleCreateProps, @@ -620,7 +615,7 @@ export const RuleCreateProps = z.union([ ]); export type RuleUpdateProps = z.infer; -export const RuleUpdateProps = z.union([ +export const RuleUpdateProps = z.discriminatedUnion('type', [ EqlRuleUpdateProps, QueryRuleUpdateProps, SavedQueryRuleUpdateProps, @@ -644,7 +639,7 @@ export const RulePatchProps = z.union([ ]); export type RuleResponse = z.infer; -export const RuleResponse = z.union([ +export const RuleResponse = z.discriminatedUnion('type', [ EqlRule, QueryRule, SavedQueryRule, diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml index 955916b939e82..a916e7e4da224 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml @@ -7,6 +7,7 @@ components: x-codegen-enabled: true schemas: BaseRequiredFields: + x-inline: true type: object properties: name: @@ -24,6 +25,7 @@ components: - severity BaseOptionalFields: + x-inline: true type: object properties: # Field overrides @@ -73,6 +75,7 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RuleActionThrottle' BaseDefaultableFields: + x-inline: true type: object properties: # Main attributes @@ -127,12 +130,14 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/ThreatArray' BaseCreateProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseRequiredFields' - $ref: '#/components/schemas/BaseOptionalFields' - $ref: '#/components/schemas/BaseDefaultableFields' BasePatchProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseRequiredFields' x-modify: partial @@ -140,13 +145,14 @@ components: - $ref: '#/components/schemas/BaseDefaultableFields' BaseResponseProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseRequiredFields' - $ref: '#/components/schemas/BaseOptionalFields' - $ref: '#/components/schemas/BaseDefaultableFields' x-modify: required - ResponseRequiredFields: + ResponseFields: type: object properties: id: @@ -179,6 +185,8 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RequiredFieldArray' setup: $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' + execution_summary: + $ref: '../../rule_monitoring/model/execution_summary.schema.yaml#/components/schemas/RuleExecutionSummary' required: - id - rule_id @@ -192,13 +200,8 @@ components: - required_fields - setup - ResponseOptionalFields: - type: object - properties: - execution_summary: - $ref: '../../rule_monitoring/model/execution_summary.schema.yaml#/components/schemas/RuleExecutionSummary' - SharedCreateProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseCreateProps' - type: object @@ -207,6 +210,7 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RuleSignatureId' SharedUpdateProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseCreateProps' - type: object @@ -217,6 +221,7 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RuleSignatureId' SharedPatchProps: + x-inline: true allOf: - $ref: '#/components/schemas/BasePatchProps' - type: object @@ -227,10 +232,10 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RuleSignatureId' SharedResponseProps: + x-inline: true allOf: - $ref: '#/components/schemas/BaseResponseProps' - - $ref: '#/components/schemas/ResponseRequiredFields' - - $ref: '#/components/schemas/ResponseOptionalFields' + - $ref: '#/components/schemas/ResponseFields' ############ # EQL Rule # @@ -841,6 +846,8 @@ components: ########################## TypeSpecificCreateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleCreateFields' - $ref: '#/components/schemas/QueryRuleCreateFields' @@ -863,6 +870,8 @@ components: - $ref: '#/components/schemas/EsqlRulePatchFields' TypeSpecificResponse: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleResponseFields' - $ref: '#/components/schemas/QueryRuleResponseFields' @@ -874,6 +883,8 @@ components: - $ref: '#/components/schemas/EsqlRuleResponseFields' RuleCreateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleCreateProps' - $ref: '#/components/schemas/QueryRuleCreateProps' @@ -885,6 +896,8 @@ components: - $ref: '#/components/schemas/EsqlRuleCreateProps' RuleUpdateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleUpdateProps' - $ref: '#/components/schemas/QueryRuleUpdateProps' @@ -907,6 +920,8 @@ components: - $ref: '#/components/schemas/EsqlRulePatchProps' RuleResponse: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRule' - $ref: '#/components/schemas/QueryRule' diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.schema.yaml index 889d7321c0bee..3a7a19611a144 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Prebuilt Rules Status API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/prepackaged/_status: get: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.schema.yaml index 158b8667bb615..3aeb1b04317f9 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Install Prebuilt Rules API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/prepackaged: put: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts index d11eea7b16711..6d3594335e49b 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts @@ -105,35 +105,35 @@ export const BulkActionBase = z.object({ }); export type BulkDeleteRules = z.infer; -export const BulkDeleteRules = BulkActionBase.and( +export const BulkDeleteRules = BulkActionBase.merge( z.object({ action: z.literal('delete'), }) ); export type BulkDisableRules = z.infer; -export const BulkDisableRules = BulkActionBase.and( +export const BulkDisableRules = BulkActionBase.merge( z.object({ action: z.literal('disable'), }) ); export type BulkEnableRules = z.infer; -export const BulkEnableRules = BulkActionBase.and( +export const BulkEnableRules = BulkActionBase.merge( z.object({ action: z.literal('enable'), }) ); export type BulkExportRules = z.infer; -export const BulkExportRules = BulkActionBase.and( +export const BulkExportRules = BulkActionBase.merge( z.object({ action: z.literal('export'), }) ); export type BulkDuplicateRules = z.infer; -export const BulkDuplicateRules = BulkActionBase.and( +export const BulkDuplicateRules = BulkActionBase.merge( z.object({ action: z.literal('duplicate'), duplicate: z @@ -254,7 +254,7 @@ export const BulkActionEditPayload = z.union([ ]); export type BulkEditRules = z.infer; -export const BulkEditRules = BulkActionBase.and( +export const BulkEditRules = BulkActionBase.merge( z.object({ action: z.literal('edit'), /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.schema.yaml index 583782f086ae7..10422772785e3 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bulk Actions API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_bulk_action: post: @@ -169,6 +169,7 @@ components: type: string BulkActionBase: + x-inline: true type: object properties: query: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.schema.yaml index ee02ec47c59b9..3b5d56f5b736d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bulk Create API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_bulk_create: post: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts index 2e6cff31f8f7d..52c7d5e097cc3 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts @@ -27,7 +27,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, 0.type: Invalid literal value, expected \\"eql\\", and 52 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -58,9 +58,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where the first is valid but the second is invalid (risk_score) will not validate', () => { @@ -72,9 +70,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"1.risk_score: Required, 1.type: Invalid literal value, expected \\"eql\\", 1.language: Invalid literal value, expected \\"eql\\", 1.risk_score: Required, 1.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"1.risk_score: Required"`); }); test('two array elements where the first is invalid (risk_score) but the second is valid will not validate', () => { @@ -86,9 +82,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where both are invalid (risk_score) will not validate', () => { @@ -103,7 +97,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 49 more"` + `"0.risk_score: Required, 1.risk_score: Required"` ); }); @@ -130,7 +124,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', and 22 more"` + `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup'"` ); }); @@ -165,7 +159,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.note: Expected string, received object, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.note: Expected string, received object, 0.note: Expected string, received object, and 22 more"` + `"0.note: Expected string, received object"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.schema.yaml index 85bdb7027447b..8438bb5b60052 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bulk Delete API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_bulk_delete: delete: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.schema.yaml index eb4ea8a06fc80..7ba82e4ad3673 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bulk Patch API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_bulk_update: patch: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.schema.yaml index 5259a677bc1f0..6f85e51c6a01e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bulk Update API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_bulk_update: put: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts index 3fa69c6ad24dc..f7e193856d0ea 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts @@ -28,7 +28,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, 0.type: Invalid literal value, expected \\"eql\\", and 52 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -59,9 +59,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where the first is valid but the second is invalid (risk_score) will not validate', () => { @@ -73,9 +71,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"1.risk_score: Required, 1.type: Invalid literal value, expected \\"eql\\", 1.language: Invalid literal value, expected \\"eql\\", 1.risk_score: Required, 1.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"1.risk_score: Required"`); }); test('two array elements where the first is invalid (risk_score) but the second is valid will not validate', () => { @@ -87,9 +83,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where both are invalid (risk_score) will not validate', () => { @@ -104,7 +98,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 49 more"` + `"0.risk_score: Required, 1.risk_score: Required"` ); }); @@ -131,7 +125,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', and 22 more"` + `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup'"` ); }); @@ -176,7 +170,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.note: Expected string, received object, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.note: Expected string, received object, 0.note: Expected string, received object, and 22 more"` + `"0.note: Expected string, received object"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts index 413d83f9fee01..2d4af1c18f6d1 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts @@ -46,7 +46,7 @@ describe('Bulk CRUD rules response schema', () => { const result = BulkCrudRulesResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.error: Required, 0: Unrecognized key(s) in object: 'author', 'created_at', 'updated_at', 'created_by', 'description', 'enabled', 'false_positives', 'from', 'immutable', 'references', 'revision', 'severity', 'severity_mapping', 'updated_by', 'tags', 'to', 'threat', 'version', 'output_index', 'max_signals', 'risk_score', 'risk_score_mapping', 'interval', 'exceptions_list', 'related_integrations', 'required_fields', 'setup', 'throttle', 'actions', 'building_block_type', 'note', 'license', 'outcome', 'alias_target_id', 'alias_purpose', 'timeline_id', 'timeline_title', 'meta', 'rule_name_override', 'timestamp_override', 'timestamp_override_fallback_disabled', 'namespace', 'investigation_fields', 'query', 'type', 'language', 'index', 'data_view_id', 'filters', 'saved_id', 'response_actions', 'alert_suppression', 0.name: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", and 24 more"` + `"0.name: Required, 0.error: Required, 0: Unrecognized key(s) in object: 'author', 'created_at', 'updated_at', 'created_by', 'description', 'enabled', 'false_positives', 'from', 'immutable', 'references', 'revision', 'severity', 'severity_mapping', 'updated_by', 'tags', 'to', 'threat', 'version', 'output_index', 'max_signals', 'risk_score', 'risk_score_mapping', 'interval', 'exceptions_list', 'related_integrations', 'required_fields', 'setup', 'throttle', 'actions', 'building_block_type', 'note', 'license', 'outcome', 'alias_target_id', 'alias_purpose', 'timeline_id', 'timeline_title', 'meta', 'rule_name_override', 'timestamp_override', 'timestamp_override_fallback_disabled', 'namespace', 'investigation_fields', 'query', 'type', 'language', 'index', 'data_view_id', 'filters', 'saved_id', 'response_actions', 'alert_suppression'"` ); }); @@ -59,7 +59,7 @@ describe('Bulk CRUD rules response schema', () => { const result = BulkCrudRulesResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.error: Required, 0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, and 267 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', 0.error: Required"` ); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.schema.yaml index f3e49fc95a048..4dff72dc216e1 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Create Rule API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules: post: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.schema.yaml index 66236f70b9b6c..be55d0add8322 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Delete Rule API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules: delete: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.schema.yaml index 98a76e3712b45..df2bdb114c2e0 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Patch Rule API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules: patch: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.schema.yaml index 8713e295e8f33..bcb4cc83381df 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Read Rule API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules: get: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.schema.yaml index 7adaca37a243b..e32a3cd52e688 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Update Rule API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules: put: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/export_rules/export_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/export_rules/export_rules_route.schema.yaml index 5b36290ddf174..73c60f76e19a8 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/export_rules/export_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/export_rules/export_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Export Rules API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_export: summary: Exports rules to an `.ndjson` file diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/find_rules/find_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/find_rules/find_rules_route.schema.yaml index 4fa1c14542ed0..4a37d1f9f5bc9 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/find_rules/find_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/find_rules/find_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Find Rules API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_find: get: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/import_rules_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/import_rules_route.schema.yaml index e158434354fde..ddc0f063747ec 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/import_rules_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/import_rules_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Import Rules API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/rules/_import: summary: Imports rules from an `.ndjson` file diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts index f53f67757ccdb..3f364c6619db6 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts @@ -22,7 +22,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 25 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more"` ); }); @@ -47,7 +47,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 24 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -61,7 +61,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 23 more"` + `"name: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -76,7 +76,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 23 more"` + `"name: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -330,7 +330,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"index.0: Expected string, received number"` ); }); @@ -378,7 +378,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"filters: Expected array, received string"` ); }); @@ -414,7 +414,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + `"language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'"` ); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts index 5c9514943ac41..9634d773b121d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts @@ -8,7 +8,7 @@ import * as z from 'zod'; import { BaseCreateProps, - ResponseRequiredFields, + ResponseFields, RuleSignatureId, TypeSpecificCreateProps, } from '../../model/rule_schema'; @@ -26,7 +26,7 @@ import { export type RuleToImport = z.infer; export type RuleToImportInput = z.input; export const RuleToImport = BaseCreateProps.and(TypeSpecificCreateProps).and( - ResponseRequiredFields.partial().extend({ + ResponseFields.partial().extend({ rule_id: RuleSignatureId, immutable: z.literal(false).default(false), }) diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/read_tags/read_tags_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/read_tags/read_tags_route.schema.yaml index b9e79f252a269..ae4ef41a9ff32 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/read_tags/read_tags_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/read_tags/read_tags_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Tags API endpoint - version: 2023-10-31 + version: '2023-10-31' paths: /api/detection_engine/tags: summary: Aggregates and returns rule tags diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts index c332f23f27025..269f041a25a10 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; +import { UploadActionRequestSchema } from '../..'; import { ExecuteActionRequestSchema } from '../execute_route'; import { EndpointActionGetFileSchema } from '../get_file_route'; import { KillOrSuspendProcessRequestSchema, NoParametersRequestSchema } from './base'; @@ -15,4 +17,7 @@ export const ResponseActionBodySchema = schema.oneOf([ KillOrSuspendProcessRequestSchema.body, EndpointActionGetFileSchema.body, ExecuteActionRequestSchema.body, + UploadActionRequestSchema.body, ]); + +export type ResponseActionsRequestBody = TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts index dbd24eef454d3..12a227048d33d 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema, Command, Timeout } from '../model/schema/common.gen'; export type ExecuteActionRequestBody = z.infer; -export const ExecuteActionRequestBody = BaseActionSchema.and( +export const ExecuteActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ command: Command, diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts index 785a4a1097e0c..4f40d187e7e3e 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema } from '../model/schema/common.gen'; export type FileUploadActionRequestBody = z.infer; -export const FileUploadActionRequestBody = BaseActionSchema.and( +export const FileUploadActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ overwrite: z.boolean().optional().default(false), diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts index 648f1700a54ca..1b1513af9b13b 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema } from '../model/schema/common.gen'; export type GetFileActionRequestBody = z.infer; -export const GetFileActionRequestBody = BaseActionSchema.and( +export const GetFileActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ path: z.string(), diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts index e68194411748f..a9e56e52ba292 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { TypeOf } from '@kbn/config-schema'; import { NoParametersRequestSchema } from './common/base'; export const GetProcessesRouteRequestSchema = NoParametersRequestSchema; +export type GetProcessesRequestBody = TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts index a58364c2be243..0df0d8d913457 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { TypeOf } from '@kbn/config-schema'; import { NoParametersRequestSchema } from './common/base'; export const IsolateRouteRequestSchema = NoParametersRequestSchema; +export type IsolationRouteRequestBody = TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts index 32844921170bd..cc365bd921733 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts @@ -23,8 +23,8 @@ import { WithOutputs, } from '../model/schema/common.gen'; -export type ListRequestQuery = z.infer; -export const ListRequestQuery = z.object({ +export type EndpointActionListRequestQuery = z.infer; +export const EndpointActionListRequestQuery = z.object({ agentIds: AgentIds.optional(), commands: Commands.optional(), page: Page.optional(), diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml index c07ad4eb253b0..71382eda8db59 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml @@ -13,7 +13,7 @@ paths: in: query required: true schema: - $ref: '#/components/schemas/ListRequestQuery' + $ref: '#/components/schemas/EndpointActionListRequestQuery' responses: '200': description: OK @@ -23,7 +23,7 @@ paths: $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' components: schemas: - ListRequestQuery: + EndpointActionListRequestQuery: type: object properties: agentIds: diff --git a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts index 986260c2e463f..c5fc0f38f6b05 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts @@ -145,7 +145,7 @@ export const BaseActionSchema = z.object({ }); export type ProcessActionSchemas = z.infer; -export const ProcessActionSchemas = BaseActionSchema.and( +export const ProcessActionSchemas = BaseActionSchema.merge( z.object({ parameters: z.union([ z.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml index 15d69f3639d1b..8d7da51775339 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml @@ -2,7 +2,7 @@ openapi: 3.0.0 info: title: Common Endpoint Attributes version: '2023-10-31' -paths: { } +paths: {} components: schemas: Id: @@ -145,6 +145,7 @@ components: description: Optional parameters object BaseActionSchema: + x-inline: true type: object properties: endpoint_ids: @@ -181,4 +182,3 @@ components: type: object properties: {} # Define properties for the success response if needed - diff --git a/x-pack/plugins/security_solution/common/api/timeline/copy_timeline/copy_timeline_route.ts b/x-pack/plugins/security_solution/common/api/timeline/copy_timeline/copy_timeline_route.ts new file mode 100644 index 0000000000000..1b7dc1d4c3566 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/timeline/copy_timeline/copy_timeline_route.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as rt from 'io-ts'; + +import { SavedTimelineRuntimeType } from '../model/api'; + +export const copyTimelineSchema = rt.type({ + timeline: SavedTimelineRuntimeType, + timelineIdToCopy: rt.string, +}); diff --git a/x-pack/plugins/security_solution/common/api/timeline/index.ts b/x-pack/plugins/security_solution/common/api/timeline/index.ts index 83748b596e5b1..6229b07c53a9f 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/index.ts @@ -20,3 +20,4 @@ export * from './persist_favorite/persist_favorite_route'; export * from './persist_note/persist_note_route'; export * from './pinned_events/pinned_events_route'; export * from './install_prepackaged_timelines/install_prepackaged_timelines'; +export * from './copy_timeline/copy_timeline_route'; diff --git a/x-pack/plugins/security_solution/common/api/timeline/model/api.ts b/x-pack/plugins/security_solution/common/api/timeline/model/api.ts index c423b2a4418bb..5237772ef5e1c 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/model/api.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/model/api.ts @@ -441,10 +441,16 @@ export const TimelineResponseType = runtimeTypes.type({ }), }); -export const TimelineErrorResponseType = runtimeTypes.type({ - status_code: runtimeTypes.number, - message: runtimeTypes.string, -}); +export const TimelineErrorResponseType = runtimeTypes.union([ + runtimeTypes.type({ + status_code: runtimeTypes.number, + message: runtimeTypes.string, + }), + runtimeTypes.type({ + statusCode: runtimeTypes.number, + message: runtimeTypes.string, + }), +]); export type TimelineErrorResponse = runtimeTypes.TypeOf; export type TimelineResponse = runtimeTypes.TypeOf; diff --git a/x-pack/plugins/security_solution/common/asset_criticality/index.ts b/x-pack/plugins/security_solution/common/asset_criticality/index.ts new file mode 100644 index 0000000000000..af45b0f6a8ab1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/asset_criticality/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './indices'; diff --git a/x-pack/plugins/security_solution/common/asset_criticality/indices.ts b/x-pack/plugins/security_solution/common/asset_criticality/indices.ts new file mode 100644 index 0000000000000..0cea9cfdc1e0a --- /dev/null +++ b/x-pack/plugins/security_solution/common/asset_criticality/indices.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const indexBase = '.asset-criticality.asset-criticality'; + +export const getAssetCriticalityIndex = (namespace: string) => `${indexBase}-${namespace}`; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index a7460bcd70345..e50533f223928 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -259,6 +259,10 @@ export const RISK_ENGINE_STATUS_URL = `${RISK_ENGINE_URL}/status`; export const RISK_ENGINE_INIT_URL = `${RISK_ENGINE_URL}/init`; export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable`; export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable`; +export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges`; + +export const ASSET_CRITICALITY_URL = `/internal/asset_criticality`; +export const ASSET_CRITICALITY_STATUS_URL = `${ASSET_CRITICALITY_URL}/status`; /** * Public Risk Score routes @@ -292,6 +296,7 @@ export const TIMELINE_DRAFT_URL = `${TIMELINE_URL}/_draft` as const; export const TIMELINE_EXPORT_URL = `${TIMELINE_URL}/_export` as const; export const TIMELINE_IMPORT_URL = `${TIMELINE_URL}/_import` as const; export const TIMELINE_PREPACKAGED_URL = `${TIMELINE_URL}/_prepackaged` as const; +export const TIMELINE_COPY_URL = `${TIMELINE_URL}/_copy` as const; export const NOTE_URL = '/api/note' as const; export const PINNED_EVENT_URL = '/api/pinned_event' as const; @@ -429,6 +434,7 @@ export const RULES_TABLE_MAX_PAGE_SIZE = 100; */ export const NEW_FEATURES_TOUR_STORAGE_KEYS = { RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.11', + TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour', }; export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY = @@ -449,8 +455,8 @@ export const RISKY_HOSTS_DOC_LINK = 'https://www.elastic.co/guide/en/security/current/host-risk-score.html'; export const RISKY_USERS_DOC_LINK = 'https://www.elastic.co/guide/en/security/current/user-risk-score.html'; -export const DETECTION_ENTITY_DASHBOARD = - 'https://www.elastic.co/guide/en/security/current/detection-entity-dashboard.html'; +export const RISKY_ENTITY_SCORE_DOC_LINK = + 'https://www.elastic.co/guide/en/security/current/advanced-entity-analytics-overview.html#entity-risk-scoring'; export const MAX_NUMBER_OF_NEW_TERMS_FIELDS = 3; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index b080981713dec..81c4bd8406408 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -70,8 +70,14 @@ export const allowedExperimentalValues = Object.freeze({ * Enables top charts on Alerts Page */ alertsPageChartsEnabled: true, + /** + * Enables the alert type column in KPI visualizations on Alerts Page + */ alertTypeEnabled: false, - + /** + * Enables expandable flyout in create rule page, alert preview + */ + expandableFlyoutInCreateRuleEnabled: false, /* * Enables new Set of filters on the Alerts page. * @@ -114,6 +120,17 @@ export const allowedExperimentalValues = Object.freeze({ * This flag is used to disable the tour in cypress tests. */ disableTimelineSaveTour: false, + + /** + * Enables the risk engine privileges route + * and associated callout in the UI + */ + riskEnginePrivilegesRouteEnabled: false, + + /* + * Enables experimental Entity Analytics Asset Criticality feature + */ + entityAnalyticsAssetCriticalityEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/common/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/risk_engine/constants.ts index 2d4d208559894..46a5a99a7e21a 100644 --- a/x-pack/plugins/security_solution/common/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/risk_engine/constants.ts @@ -6,3 +6,12 @@ */ export const MAX_SPACES_COUNT = 1; + +export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ + 'manage_index_templates', + 'manage_transform', +]; + +export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ + 'risk-score.risk-score-*': ['read', 'write'], +}); diff --git a/x-pack/plugins/security_solution/common/test/ess_roles.json b/x-pack/plugins/security_solution/common/test/ess_roles.json index d21fe90e2de02..9bf9e1b64aee3 100644 --- a/x-pack/plugins/security_solution/common/test/ess_roles.json +++ b/x-pack/plugins/security_solution/common/test/ess_roles.json @@ -132,5 +132,22 @@ "base": [] } ] + }, + "no_risk_engine_privileges": { + "name": "no_risk_engine_privileges", + "elasticsearch": { + "cluster": [], + "indices": [], + "run_as": [] + }, + "kibana": [ + { + "feature": { + "siem": ["read"] + }, + "spaces": ["*"], + "base": [] + } + ] } } diff --git a/x-pack/plugins/security_solution/common/test/index.ts b/x-pack/plugins/security_solution/common/test/index.ts index ac2fd661320ce..277f54c78e6c5 100644 --- a/x-pack/plugins/security_solution/common/test/index.ts +++ b/x-pack/plugins/security_solution/common/test/index.ts @@ -29,6 +29,7 @@ export enum ROLES { reader = 'reader', hunter = 'hunter', hunter_no_actions = 'hunter_no_actions', + no_risk_engine_privileges = 'no_risk_engine_privileges', } /** diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index a13046b0bc59f..5f0613c3e89b8 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -26,6 +26,7 @@ "mappings:load": "node scripts/mappings/mappings_loader", "junit:transform": "node scripts/junit_transformer --pathPattern '../../../target/kibana-security-solution/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Security Solution Cypress' --writeInPlace", "openapi:generate": "node scripts/openapi/generate", - "openapi:generate:debug": "node --inspect-brk scripts/openapi/generate" + "openapi:generate:debug": "node --inspect-brk scripts/openapi/generate", + "openapi:bundle": "node scripts/openapi/bundle" } } \ No newline at end of file diff --git a/x-pack/plugins/security_solution/public/app/index.tsx b/x-pack/plugins/security_solution/public/app/index.tsx index 6f0fc3eb8d01c..3053590ae3d91 100644 --- a/x-pack/plugins/security_solution/public/app/index.tsx +++ b/x-pack/plugins/security_solution/public/app/index.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { SubscriptionTrackingProvider } from '@kbn/subscription-tracking'; import { SecurityApp } from './app'; import type { RenderAppProps } from './types'; import { AppRoutes } from './app_routes'; @@ -21,7 +20,6 @@ export const renderApp = ({ usageCollection, subPluginRoutes, theme$, - subscriptionTrackingServices, }: RenderAppProps): (() => void) => { const ApplicationUsageTrackingProvider = usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; @@ -34,12 +32,7 @@ export const renderApp = ({ theme$={theme$} > - - - + , element diff --git a/x-pack/plugins/security_solution/public/app/routes.tsx b/x-pack/plugins/security_solution/public/app/routes.tsx index 73fe2615b0e5a..307d8f4c32376 100644 --- a/x-pack/plugins/security_solution/public/app/routes.tsx +++ b/x-pack/plugins/security_solution/public/app/routes.tsx @@ -14,7 +14,7 @@ import type { AppLeaveHandler } from '@kbn/core/public'; import { APP_ID } from '../../common/constants'; import { RouteCapture } from '../common/components/endpoint/route_capture'; -import { useGetUserCasesPermissions, useKibana } from '../common/lib/kibana'; +import { useKibana } from '../common/lib/kibana'; import type { AppAction } from '../common/store/actions'; import { ManageRoutesSpy } from '../common/utils/route/manage_spy_routes'; import { NotFoundPage } from './404'; @@ -29,7 +29,7 @@ interface RouterProps { const PageRouterComponent: FC = ({ children, history, onAppLeave }) => { const { cases } = useKibana().services; const CasesContext = cases.ui.getCasesContext(); - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const dispatch = useDispatch<(action: AppAction) => void>(); useEffect(() => { return () => { diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 66bab19c945fe..578a4800f7f64 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -19,7 +19,6 @@ import type { RouteProps } from 'react-router-dom'; import type { AppMountParameters } from '@kbn/core/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { TableState } from '@kbn/securitysolution-data-table'; -import type { Services as SubscriptionTrackingServices } from '@kbn/subscription-tracking'; import type { ExploreReducer, ExploreState } from '../explore'; import type { StartServices } from '../types'; @@ -30,7 +29,6 @@ export interface RenderAppProps extends AppMountParameters { services: StartServices; store: Store; subPluginRoutes: RouteProps[]; - subscriptionTrackingServices: SubscriptionTrackingServices; usageCollection?: UsageCollectionSetup; } diff --git a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx index 5a3e4a3d1c210..6059ef63ae6f1 100644 --- a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx @@ -12,13 +12,14 @@ import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { useAssistantContext } from '@kbn/elastic-assistant/impl/assistant_context'; -import { useKibana, useToasts } from '../../common/lib/kibana'; +import { useBasePath, useKibana, useToasts } from '../../common/lib/kibana'; import type { Note } from '../../common/lib/note'; import { appActions } from '../../common/store/actions'; import { TimelineId } from '../../../common/types'; import { updateAndAssociateNode } from '../../timelines/components/notes/helpers'; import { timelineActions } from '../../timelines/store/timeline'; import * as i18n from './translations'; +import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; interface Props { message: Message; @@ -26,8 +27,10 @@ interface Props { const CommentActionsComponent: React.FC = ({ message }) => { const toasts = useToasts(); + const basePath = useBasePath(); const { cases } = useKibana().services; const dispatch = useDispatch(); + const isModelEvaluationEnabled = useIsExperimentalFeatureEnabled('assistantModelEvaluation'); const { showAssistantOverlay } = useAssistantContext(); @@ -75,8 +78,39 @@ const CommentActionsComponent: React.FC = ({ message }) => { }); }, [content, selectCaseModal, showAssistantOverlay]); + // Note: This feature is behind the `isModelEvaluationEnabled` FF. If ever released, this URL should be configurable + // as APM data may not go to the same cluster where the Kibana instance is running + // Links to the experimental trace explorer page + // Note: There's a bug with URL params being rewritten, so must specify 'query' to filter on transaction id + // See: https://github.com/elastic/kibana/issues/171368 + const apmTraceLink = + message.traceData != null + ? `${basePath}/app/apm/traces/explorer/waterfall?comparisonEnabled=false&detailTab=timeline&environment=ENVIRONMENT_ALL&kuery=&query=transaction.id:%20${message.traceData.transactionId}&rangeFrom=now-1y/d&rangeTo=now&showCriticalPath=false&traceId=${message.traceData.traceId}&transactionId=${message.traceData.transactionId}&type=kql&waterfallItemId=` + : undefined; + + // Use this link for routing to the services/transactions view which provides a slightly different view + // const apmTraceLink = + // message.traceData != null + // ? `${basePath}/app/apm/services/kibana/transactions/view?kuery=&rangeFrom=now-1y&rangeTo=now&environment=ENVIRONMENT_ALL&serviceGroup=&comparisonEnabled=true&traceId=${message.traceData.traceId}&transactionId=${message.traceData.transactionId}&transactionName=POST%20/internal/elastic_assistant/actions/connector/?/_execute&transactionType=request&offset=1d&latencyAggregationType=avg` + // : undefined; + return ( + // APM Trace support is currently behind the Model Evaluation feature flag until wider testing is performed + {isModelEvaluationEnabled && apmTraceLink != null && ( + + + + + + )} + ; regenerateMessage: () => void; transformMessage: (message: string) => ContentMessage; @@ -29,6 +30,7 @@ interface Props { export const StreamComment = ({ amendMessage, content, + connectorTypeTitle, index, isError = false, isFetching = false, @@ -40,6 +42,7 @@ export const StreamComment = ({ const { error, isLoading, isStreaming, pendingMessage, setComplete } = useStream({ amendMessage, content, + connectorTypeTitle, reader, isError, }); diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.test.ts b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.test.ts index 764db1b3990ae..54a5684d20442 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.test.ts +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.test.ts @@ -9,6 +9,8 @@ import { API_ERROR } from '../translations'; import type { PromptObservableState } from './types'; import { Subject } from 'rxjs'; +import { EventStreamCodec } from '@smithy/eventstream-codec'; +import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; describe('getStreamObservable', () => { const mockReader = { read: jest.fn(), @@ -22,29 +24,102 @@ describe('getStreamObservable', () => { beforeEach(() => { jest.clearAllMocks(); }); + it('should emit loading state and chunks for Bedrock', (done) => { + const completeSubject = new Subject(); + const expectedStates: PromptObservableState[] = [ + { chunks: [], loading: true }, + { + // when i log the actual emit, chunks equal to message.split(''); test is wrong + chunks: ['My', ' new', ' message'], + message: 'My', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new message', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new message', + loading: false, + }, + ]; - it('should emit loading state and chunks', (done) => { + mockReader.read + .mockResolvedValueOnce({ + done: false, + value: encodeBedrockResponse('My'), + }) + .mockResolvedValueOnce({ + done: false, + value: encodeBedrockResponse(' new'), + }) + .mockResolvedValueOnce({ + done: false, + value: encodeBedrockResponse(' message'), + }) + .mockResolvedValue({ + done: true, + }); + + const source = getStreamObservable({ + connectorTypeTitle: 'Amazon Bedrock', + isError: false, + reader: typedReader, + setLoading, + }); + const emittedStates: PromptObservableState[] = []; + + source.subscribe({ + next: (state) => { + return emittedStates.push(state); + }, + complete: () => { + expect(emittedStates).toEqual(expectedStates); + done(); + + completeSubject.subscribe({ + next: () => { + expect(setLoading).toHaveBeenCalledWith(false); + expect(typedReader.cancel).toHaveBeenCalled(); + done(); + }, + }); + }, + error: (err) => done(err), + }); + }); + it('should emit loading state and chunks for OpenAI', (done) => { + const chunk1 = `data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"}}]}`; + const chunk2 = `\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]`; const completeSubject = new Subject(); const expectedStates: PromptObservableState[] = [ { chunks: [], loading: true }, { - chunks: ['one chunk ', 'another chunk', ''], - message: 'one chunk ', + // when i log the actual emit, chunks equal to message.split(''); test is wrong + chunks: ['My', ' new', ' message'], + message: 'My', loading: true, }, { - chunks: ['one chunk ', 'another chunk', ''], - message: 'one chunk another chunk', + chunks: ['My', ' new', ' message'], + message: 'My new', loading: true, }, { - chunks: ['one chunk ', 'another chunk', ''], - message: 'one chunk another chunk', + chunks: ['My', ' new', ' message'], + message: 'My new message', loading: true, }, { - chunks: ['one chunk ', 'another chunk', ''], - message: 'one chunk another chunk', + chunks: ['My', ' new', ' message'], + message: 'My new message', loading: false, }, ]; @@ -52,11 +127,11 @@ describe('getStreamObservable', () => { mockReader.read .mockResolvedValueOnce({ done: false, - value: new Uint8Array(new TextEncoder().encode(`one chunk `)), + value: new Uint8Array(new TextEncoder().encode(chunk1)), }) .mockResolvedValueOnce({ done: false, - value: new Uint8Array(new TextEncoder().encode(`another chunk`)), + value: new Uint8Array(new TextEncoder().encode(chunk2)), }) .mockResolvedValueOnce({ done: false, @@ -66,11 +141,91 @@ describe('getStreamObservable', () => { done: true, }); - const source = getStreamObservable(typedReader, setLoading, false); + const source = getStreamObservable({ + connectorTypeTitle: 'OpenAI', + isError: false, + reader: typedReader, + setLoading, + }); const emittedStates: PromptObservableState[] = []; source.subscribe({ - next: (state) => emittedStates.push(state), + next: (state) => { + return emittedStates.push(state); + }, + complete: () => { + expect(emittedStates).toEqual(expectedStates); + done(); + + completeSubject.subscribe({ + next: () => { + expect(setLoading).toHaveBeenCalledWith(false); + expect(typedReader.cancel).toHaveBeenCalled(); + done(); + }, + }); + }, + error: (err) => done(err), + }); + }); + it('should emit loading state and chunks for partial response OpenAI', (done) => { + const chunk1 = `data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"`; + const chunk2 = `}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]`; + const completeSubject = new Subject(); + const expectedStates: PromptObservableState[] = [ + { chunks: [], loading: true }, + { + // when i log the actual emit, chunks equal to message.split(''); test is wrong + chunks: ['My', ' new', ' message'], + message: 'My', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new message', + loading: true, + }, + { + chunks: ['My', ' new', ' message'], + message: 'My new message', + loading: false, + }, + ]; + + mockReader.read + .mockResolvedValueOnce({ + done: false, + value: new Uint8Array(new TextEncoder().encode(chunk1)), + }) + .mockResolvedValueOnce({ + done: false, + value: new Uint8Array(new TextEncoder().encode(chunk2)), + }) + .mockResolvedValueOnce({ + done: false, + value: new Uint8Array(new TextEncoder().encode('')), + }) + .mockResolvedValue({ + done: true, + }); + + const source = getStreamObservable({ + connectorTypeTitle: 'OpenAI', + isError: false, + reader: typedReader, + setLoading, + }); + const emittedStates: PromptObservableState[] = []; + + source.subscribe({ + next: (state) => { + return emittedStates.push(state); + }, complete: () => { expect(emittedStates).toEqual(expectedStates); done(); @@ -112,7 +267,12 @@ describe('getStreamObservable', () => { done: true, }); - const source = getStreamObservable(typedReader, setLoading, true); + const source = getStreamObservable({ + connectorTypeTitle: 'OpenAI', + isError: true, + reader: typedReader, + setLoading, + }); const emittedStates: PromptObservableState[] = []; source.subscribe({ @@ -138,7 +298,12 @@ describe('getStreamObservable', () => { const error = new Error('Test Error'); // Simulate an error mockReader.read.mockRejectedValue(error); - const source = getStreamObservable(typedReader, setLoading, false); + const source = getStreamObservable({ + connectorTypeTitle: 'OpenAI', + isError: false, + reader: typedReader, + setLoading, + }); source.subscribe({ next: (state) => {}, @@ -157,3 +322,16 @@ describe('getStreamObservable', () => { }); }); }); + +function encodeBedrockResponse(completion: string) { + return new EventStreamCodec(toUtf8, fromUtf8).encode({ + headers: {}, + body: Uint8Array.from( + Buffer.from( + JSON.stringify({ + bytes: Buffer.from(JSON.stringify({ completion })).toString('base64'), + }) + ) + ), + }); +} diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.ts b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.ts index b30be69b82cae..ce7a38811f229 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.ts +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/stream_observable.ts @@ -7,10 +7,18 @@ import { concatMap, delay, finalize, Observable, of, scan, timestamp } from 'rxjs'; import type { Dispatch, SetStateAction } from 'react'; -import { API_ERROR } from '../translations'; +import { EventStreamCodec } from '@smithy/eventstream-codec'; +import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; import type { PromptObservableState } from './types'; +import { API_ERROR } from '../translations'; const MIN_DELAY = 35; +interface StreamObservable { + connectorTypeTitle: string; + reader: ReadableStreamDefaultReader; + setLoading: Dispatch>; + isError: boolean; +} /** * Returns an Observable that reads data from a ReadableStream and emits values representing the state of the data processing. * @@ -19,52 +27,155 @@ const MIN_DELAY = 35; * @param isError - indicates whether the reader response is an error message or not * @returns {Observable} An Observable that emits PromptObservableState */ -export const getStreamObservable = ( - reader: ReadableStreamDefaultReader, - setLoading: Dispatch>, - isError: boolean -): Observable => +export const getStreamObservable = ({ + connectorTypeTitle, + isError, + reader, + setLoading, +}: StreamObservable): Observable => new Observable((observer) => { observer.next({ chunks: [], loading: true }); const decoder = new TextDecoder(); const chunks: string[] = []; - function read() { + // Initialize an empty string to store the OpenAI buffer. + let openAIBuffer: string = ''; + + // Initialize an empty Uint8Array to store the Bedrock concatenated buffer. + let bedrockBuffer: Uint8Array = new Uint8Array(0); + function readOpenAI() { reader .read() .then(({ done, value }: { done: boolean; value?: Uint8Array }) => { try { if (done) { + if (openAIBuffer) { + chunks.push(getOpenAIChunks([openAIBuffer])[0]); + } observer.next({ chunks, - message: getMessageFromChunks(chunks), + message: chunks.join(''), loading: false, }); observer.complete(); return; } + const decoded = decoder.decode(value); - const content = isError - ? // we format errors as {message: string; status_code: number} - `${API_ERROR}\n\n${JSON.parse(decoded).message}` - : // all other responses are just strings (handled by subaction invokeStream) - decoded; - chunks.push(content); - observer.next({ - chunks, - message: getMessageFromChunks(chunks), - loading: true, + let nextChunks; + if (isError) { + nextChunks = [`${API_ERROR}\n\n${JSON.parse(decoded).message}`]; + } else { + const lines = decoded.split('\n'); + lines[0] = openAIBuffer + lines[0]; + openAIBuffer = lines.pop() || ''; + nextChunks = getOpenAIChunks(lines); + } + nextChunks.forEach((chunk: string) => { + chunks.push(chunk); + observer.next({ + chunks, + message: chunks.join(''), + loading: true, + }); }); } catch (err) { observer.error(err); return; } - read(); + readOpenAI(); + }) + .catch((err) => { + observer.error(err); + }); + } + function readBedrock() { + reader + .read() + .then(({ done, value }: { done: boolean; value?: Uint8Array }) => { + try { + if (done) { + observer.next({ + chunks, + message: chunks.join(''), + loading: false, + }); + observer.complete(); + return; + } + + let content; + if (isError) { + content = `${API_ERROR}\n\n${JSON.parse(decoder.decode(value)).message}`; + chunks.push(content); + observer.next({ + chunks, + message: chunks.join(''), + loading: true, + }); + } else if (value != null) { + const chunk: Uint8Array = value; + + // Concatenate the current chunk to the existing buffer. + bedrockBuffer = concatChunks(bedrockBuffer, chunk); + // Get the length of the next message in the buffer. + let messageLength = getMessageLength(bedrockBuffer); + + // Initialize an array to store fully formed message chunks. + const buildChunks = []; + // Process the buffer until no complete messages are left. + while (bedrockBuffer.byteLength > 0 && bedrockBuffer.byteLength >= messageLength) { + // Extract a chunk of the specified length from the buffer. + const extractedChunk = bedrockBuffer.slice(0, messageLength); + // Add the extracted chunk to the array of fully formed message chunks. + buildChunks.push(extractedChunk); + // Remove the processed chunk from the buffer. + bedrockBuffer = bedrockBuffer.slice(messageLength); + // Get the length of the next message in the updated buffer. + messageLength = getMessageLength(bedrockBuffer); + } + + const awsDecoder = new EventStreamCodec(toUtf8, fromUtf8); + // Decode and parse each message chunk, extracting the 'completion' property. + buildChunks.forEach((bChunk) => { + const event = awsDecoder.decode(bChunk); + const body = JSON.parse( + Buffer.from(JSON.parse(decoder.decode(event.body)).bytes, 'base64').toString() + ); + content = body.completion; + chunks.push(content); + observer.next({ + chunks, + message: chunks.join(''), + loading: true, + }); + }); + } + } catch (err) { + observer.error(err); + return; + } + readBedrock(); }) .catch((err) => { observer.error(err); }); } - read(); + // this should never actually happen + function badConnector() { + observer.next({ + chunks: [ + `Invalid connector type - ${connectorTypeTitle} is not a supported GenAI connector.`, + ], + message: `Invalid connector type - ${connectorTypeTitle} is not a supported GenAI connector.`, + loading: false, + }); + observer.complete(); + } + + if (connectorTypeTitle === 'Amazon Bedrock') readBedrock(); + else if (connectorTypeTitle === 'OpenAI') readOpenAI(); + else badConnector(); + return () => { reader.cancel(); }; @@ -99,8 +210,55 @@ export const getStreamObservable = ( finalize(() => setLoading(false)) ); -function getMessageFromChunks(chunks: string[]) { - return chunks.join(''); +/** + * Parses an OpenAI response from a string. + * @param lines + * @returns {string[]} - Parsed string array from the OpenAI response. + */ +const getOpenAIChunks = (lines: string[]): string[] => { + const nextChunk = lines + .map((str) => str.substring(6)) + .filter((str) => !!str && str !== '[DONE]') + .map((line) => { + try { + const openaiResponse = JSON.parse(line); + return openaiResponse.choices[0]?.delta.content ?? ''; + } catch (err) { + return ''; + } + }); + return nextChunk; +}; + +/** + * Concatenates two Uint8Array buffers. + * + * @param {Uint8Array} a - First buffer. + * @param {Uint8Array} b - Second buffer. + * @returns {Uint8Array} - Concatenated buffer. + */ +function concatChunks(a: Uint8Array, b: Uint8Array): Uint8Array { + const newBuffer = new Uint8Array(a.length + b.length); + // Copy the contents of the first buffer to the new buffer. + newBuffer.set(a); + // Copy the contents of the second buffer to the new buffer starting from the end of the first buffer. + newBuffer.set(b, a.length); + return newBuffer; +} + +/** + * Gets the length of the next message from the buffer. + * + * @param {Uint8Array} buffer - Buffer containing the message. + * @returns {number} - Length of the next message. + */ +function getMessageLength(buffer: Uint8Array): number { + // If the buffer is empty, return 0. + if (buffer.byteLength === 0) return 0; + // Create a DataView to read the Uint32 value at the beginning of the buffer. + const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + // Read and return the Uint32 value (message length). + return view.getUint32(0, false); } export const getPlaceholderObservable = () => new Observable(); diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx index efbc61999f2cc..c4f99884aa045 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.test.tsx @@ -11,20 +11,22 @@ import { useStream } from './use_stream'; const amendMessage = jest.fn(); const reader = jest.fn(); const cancel = jest.fn(); +const chunk1 = `data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"}}]}`; +const chunk2 = `\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]`; const readerComplete = { read: reader .mockResolvedValueOnce({ done: false, - value: new Uint8Array(new TextEncoder().encode('one chunk ')), + value: new Uint8Array(new TextEncoder().encode(chunk1)), }) .mockResolvedValueOnce({ done: false, - value: new Uint8Array(new TextEncoder().encode(`another chunk`)), + value: new Uint8Array(new TextEncoder().encode(chunk2)), }) .mockResolvedValueOnce({ done: false, - value: new Uint8Array(new TextEncoder().encode(``)), + value: new Uint8Array(new TextEncoder().encode('')), }) .mockResolvedValue({ done: true, @@ -34,7 +36,12 @@ const readerComplete = { closed: jest.fn().mockResolvedValue(true), } as unknown as ReadableStreamDefaultReader; -const defaultProps = { amendMessage, reader: readerComplete, isError: false }; +const defaultProps = { + amendMessage, + reader: readerComplete, + isError: false, + connectorTypeTitle: 'OpenAI', +}; describe('useStream', () => { beforeEach(() => { jest.clearAllMocks(); @@ -57,7 +64,7 @@ describe('useStream', () => { error: undefined, isLoading: true, isStreaming: true, - pendingMessage: 'one chunk ', + pendingMessage: 'My', setComplete: expect.any(Function), }); }); @@ -67,7 +74,7 @@ describe('useStream', () => { error: undefined, isLoading: false, isStreaming: false, - pendingMessage: 'one chunk another chunk', + pendingMessage: 'My new message', setComplete: expect.any(Function), }); }); diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.tsx index 7de06589f87c7..9271758a8558e 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/stream/use_stream.tsx @@ -7,13 +7,13 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { Subscription } from 'rxjs'; -import { share } from 'rxjs'; import { getPlaceholderObservable, getStreamObservable } from './stream_observable'; interface UseStreamProps { amendMessage: (message: string) => void; isError: boolean; content?: string; + connectorTypeTitle: string; reader?: ReadableStreamDefaultReader; } interface UseStream { @@ -39,6 +39,7 @@ interface UseStream { export const useStream = ({ amendMessage, content, + connectorTypeTitle, reader, isError, }: UseStreamProps): UseStream => { @@ -49,9 +50,9 @@ export const useStream = ({ const observer$ = useMemo( () => content == null && reader != null - ? getStreamObservable(reader, setLoading, isError) + ? getStreamObservable({ connectorTypeTitle, reader, setLoading, isError }) : getPlaceholderObservable(), - [content, isError, reader] + [content, isError, reader, connectorTypeTitle] ); const onCompleteStream = useCallback(() => { subscription?.unsubscribe(); @@ -66,7 +67,7 @@ export const useStream = ({ } }, [complete, onCompleteStream]); useEffect(() => { - const newSubscription = observer$.pipe(share()).subscribe({ + const newSubscription = observer$.subscribe({ next: ({ message, loading: isLoading }) => { setLoading(isLoading); setPendingMessage(message); diff --git a/x-pack/plugins/security_solution/public/cases/links.ts b/x-pack/plugins/security_solution/public/cases/links.ts index 3017fa28816e3..2d2f6d94b351a 100644 --- a/x-pack/plugins/security_solution/public/cases/links.ts +++ b/x-pack/plugins/security_solution/public/cases/links.ts @@ -8,7 +8,7 @@ import { CREATE_CASES_CAPABILITY, READ_CASES_CAPABILITY, - UPDATE_CASES_CAPABILITY, + CASES_SETTINGS_CAPABILITY, } from '@kbn/cases-plugin/common'; import { getCasesDeepLinks } from '@kbn/cases-plugin/public'; import { CASES_FEATURE_ID, CASES_PATH, SecurityPageName } from '../../common/constants'; @@ -22,7 +22,7 @@ const casesLinks = getCasesDeepLinks({ capabilities: [`${CASES_FEATURE_ID}.${READ_CASES_CAPABILITY}`], }, [SecurityPageName.caseConfigure]: { - capabilities: [`${CASES_FEATURE_ID}.${UPDATE_CASES_CAPABILITY}`], + capabilities: [`${CASES_FEATURE_ID}.${CASES_SETTINGS_CAPABILITY}`], sideNavDisabled: true, }, [SecurityPageName.caseCreate]: { diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index dd639862e2812..ab5b170e423c0 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -21,7 +21,7 @@ import { TimelineId } from '../../../common/types/timeline'; import { getRuleDetailsUrl, useFormatUrl } from '../../common/components/link_to'; -import { useGetUserCasesPermissions, useKibana, useNavigation } from '../../common/lib/kibana'; +import { useKibana, useNavigation } from '../../common/lib/kibana'; import { APP_ID, CASES_PATH, @@ -56,7 +56,7 @@ const TimelineDetailsPanel = () => { const CaseContainerComponent: React.FC = () => { const { cases } = useKibana().services; const { getAppUrl, navigateTo } = useNavigation(); - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const dispatch = useDispatch(); const { formatUrl: detectionsFormatUrl, search: detectionsUrlSearch } = useFormatUrl( SecurityPageName.rules diff --git a/x-pack/plugins/security_solution/public/cases_test_utils.ts b/x-pack/plugins/security_solution/public/cases_test_utils.ts index d177934cb02ee..dc70dcab33eaa 100644 --- a/x-pack/plugins/security_solution/public/cases_test_utils.ts +++ b/x-pack/plugins/security_solution/public/cases_test_utils.ts @@ -5,34 +5,39 @@ * 2.0. */ -export const noCasesCapabilities = () => ({ +import type { CasesPermissions, CasesCapabilities } from '@kbn/cases-plugin/common'; + +export const noCasesCapabilities = (): CasesCapabilities => ({ create_cases: false, read_cases: false, update_cases: false, delete_cases: false, push_cases: false, - cases_connector: false, + cases_connectors: false, + cases_settings: false, }); -export const readCasesCapabilities = () => ({ +export const readCasesCapabilities = (): CasesCapabilities => ({ create_cases: false, read_cases: true, update_cases: false, delete_cases: false, push_cases: false, - cases_connector: true, + cases_connectors: true, + cases_settings: false, }); -export const allCasesCapabilities = () => ({ +export const allCasesCapabilities = (): CasesCapabilities => ({ create_cases: true, read_cases: true, update_cases: true, delete_cases: true, push_cases: true, - cases_connector: true, + cases_connectors: true, + cases_settings: true, }); -export const noCasesPermissions = () => ({ +export const noCasesPermissions = (): CasesPermissions => ({ all: false, create: false, read: false, @@ -40,9 +45,10 @@ export const noCasesPermissions = () => ({ delete: false, push: false, connectors: false, + settings: false, }); -export const readCasesPermissions = () => ({ +export const readCasesPermissions = (): CasesPermissions => ({ all: false, create: false, read: true, @@ -50,9 +56,10 @@ export const readCasesPermissions = () => ({ delete: false, push: false, connectors: true, + settings: false, }); -export const writeCasesPermissions = () => ({ +export const writeCasesPermissions = (): CasesPermissions => ({ all: false, create: true, read: false, @@ -60,9 +67,10 @@ export const writeCasesPermissions = () => ({ delete: true, push: true, connectors: true, + settings: true, }); -export const allCasesPermissions = () => ({ +export const allCasesPermissions = (): CasesPermissions => ({ all: true, create: true, read: true, @@ -70,4 +78,5 @@ export const allCasesPermissions = () => ({ delete: true, push: true, connectors: true, + settings: true, }); diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx index 83e2ee4a69ff1..47d73a6d8262e 100644 --- a/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/rounded_badge.tsx @@ -22,7 +22,8 @@ const RoundBadge = styled(EuiBadge)` margin: 0 5px 0 5px; padding: 7px 6px 4px 6px; user-select: none; - width: 34px; + width: 40px; + height: 40px; .euiBadge__content { position: relative; top: -1px; 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 80e3e0f9f5641..93a29b9ea4436 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 @@ -24,6 +24,7 @@ import { getMappedNonEcsValue } from '../../../../timelines/components/timeline/ import type { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy'; import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/types/timeline'; import { TimelineId } from '../../../../../common/types'; +import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; type Props = EuiDataGridCellValueElementProps & { columnHeaders: ColumnHeaderOptions[]; @@ -73,6 +74,9 @@ const RowActionComponent = ({ const dispatch = useDispatch(); const [isSecurityFlyoutEnabled] = useUiSetting$(ENABLE_EXPANDABLE_FLYOUT_SETTING); + const isExpandableFlyoutInCreateRuleEnabled = useIsExperimentalFeatureEnabled( + 'expandableFlyoutInCreateRuleEnabled' + ); const columnValues = useMemo( () => @@ -89,6 +93,13 @@ const RowActionComponent = ({ [columnHeaders, timelineNonEcsData] ); + let showExpandableFlyout: boolean; + if (tableId === TableId.rulePreview) { + showExpandableFlyout = isSecurityFlyoutEnabled && isExpandableFlyoutInCreateRuleEnabled; + } else { + showExpandableFlyout = isSecurityFlyoutEnabled; + } + const handleOnEventDetailPanelOpened = useCallback(() => { const updatedExpandedDetail: ExpandedDetailType = { panelView: 'eventDetail', @@ -98,9 +109,7 @@ const RowActionComponent = ({ }, }; - // TODO remove when https://github.com/elastic/security-team/issues/7760 is merged - // excluding rule preview page as some sections in new flyout are not applicable when user is creating a new rule - if (isSecurityFlyoutEnabled && tableId !== TableId.rulePreview) { + if (showExpandableFlyout) { openFlyout({ right: { id: DocumentDetailsRightPanelKey, @@ -133,7 +142,7 @@ const RowActionComponent = ({ }) ); } - }, [dispatch, eventId, indexName, isSecurityFlyoutEnabled, openFlyout, tabType, tableId]); + }, [dispatch, eventId, indexName, openFlyout, tabType, tableId, showExpandableFlyout]); const Action = controlColumn.rowCellRender; diff --git a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx index 8463097d98d07..fddf6a1afc6e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx @@ -23,6 +23,7 @@ import { useKibana } from '../../lib/kibana'; import type { State } from '../../store'; import { createStore } from '../../store'; import { TimelineId } from '../../../../common/types'; +import * as timelineActions from '../../../timelines/store/timeline/actions'; import type { ComponentType, FC, PropsWithChildren } from 'react'; import React from 'react'; import type { DataView } from '@kbn/data-views-plugin/common'; @@ -35,6 +36,17 @@ let mockDiscoverStateContainerRef = { }; jest.mock('../../lib/kibana'); + +const mockDispatch = jest.fn(); + +jest.mock('react-redux', () => { + const actual = jest.requireActual('react-redux'); + return { + ...actual, + useDispatch: () => mockDispatch, + }; +}); + const mockState: State = { ...mockGlobalState, timeline: { @@ -239,7 +251,7 @@ describe('useDiscoverInTimelineActions', () => { }) ); }); - it('should send update request when savedSearchId is already available', async () => { + it('should initialize saved search when it is not set on the timeline model yet', async () => { const localMockState: State = { ...mockGlobalState, timeline: { @@ -262,22 +274,49 @@ describe('useDiscoverInTimelineActions', () => { await result.current.updateSavedSearch(savedSearchMock, TimelineId.active); }); - expect(startServicesMock.savedSearch.save).toHaveBeenNthCalledWith( + expect(mockDispatch).toHaveBeenNthCalledWith( 1, - expect.objectContaining({ - timeRestore: true, - timeRange: { - from: 'now-20d', - to: 'now', + timelineActions.initializeSavedSearch({ + id: TimelineId.active, + savedSearch: savedSearchMock, + }) + ); + }); + + it('should update saved search when it has changes', async () => { + const changedSavedSearchMock = { ...savedSearchMock, title: 'changed' }; + const localMockState: State = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: { + ...mockGlobalState.timeline.timelineById[TimelineId.active], + title: 'Active Timeline', + description: 'Active Timeline Description', + savedSearchId: 'saved_search_id', + savedSearch: savedSearchMock, + }, }, - tags: ['security-solution-default'], - id: 'saved_search_id', - }), - expect.objectContaining({ - copyOnSave: false, + }, + }; + + const LocalTestProvider = getTestProviderWithCustomState(localMockState); + const { result } = renderTestHook(LocalTestProvider); + await act(async () => { + await result.current.updateSavedSearch(changedSavedSearchMock, TimelineId.active); + }); + + expect(mockDispatch).toHaveBeenNthCalledWith( + 1, + timelineActions.updateSavedSearch({ + id: TimelineId.active, + savedSearch: changedSavedSearchMock, }) ); }); + it('should raise appropriate notification in case of any error in saving discover saved search', () => {}); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx index 55b65f6f4683c..3479612b8e7b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx @@ -7,14 +7,13 @@ import type { DiscoverStateContainer } from '@kbn/discover-plugin/public'; import type { SaveSavedSearchOptions } from '@kbn/saved-search-plugin/public'; +import { useMemo, useCallback, useRef } from 'react'; import type { RefObject } from 'react'; -import { useMemo, useCallback } from 'react'; import { useDispatch } from 'react-redux'; import type { SavedSearch } from '@kbn/saved-search-plugin/common'; import type { DiscoverAppState } from '@kbn/discover-plugin/public/application/main/services/discover_app_state_container'; import type { TimeRange } from '@kbn/es-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { endTimelineSaving, startTimelineSaving } from '../../../timelines/store/timeline/actions'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { TimelineId } from '../../../../common/types'; import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; @@ -57,6 +56,10 @@ export const useDiscoverInTimelineActions = ( ); const { savedSearchId } = timeline; + // We're using a ref here to prevent a cyclic hook-dependency chain of updateSavedSearch + const timelineRef = useRef(timeline); + timelineRef.current = timeline; + const queryClient = useQueryClient(); const { mutateAsync: saveSavedSearch } = useMutation({ @@ -177,60 +180,90 @@ export const useDiscoverInTimelineActions = ( * */ const updateSavedSearch = useCallback( async (savedSearch: SavedSearch, timelineId: string) => { - dispatch( - startTimelineSaving({ - id: timelineId, - }) - ); savedSearch.timeRestore = true; savedSearch.timeRange = savedSearch.timeRange ?? discoverDataService.query.timefilter.timefilter.getTime(); savedSearch.tags = ['security-solution-default']; + // If there is already a saved search, only update the local state if (savedSearchId) { savedSearch.id = savedSearchId; - } - try { - const response = await persistSavedSearch(savedSearch, { - onTitleDuplicate: () => {}, - copyOnSave: !savedSearchId, - }); - - if (!response || !response.id) { - throw new Error('Unknown Error occured'); + if (!timelineRef.current.savedSearch) { + dispatch( + timelineActions.initializeSavedSearch({ + id: TimelineId.active, + savedSearch, + }) + ); + } else { + dispatch( + timelineActions.updateSavedSearch({ + id: TimelineId.active, + savedSearch, + }) + ); } + } else { + // If no saved search exists. Create a new saved search instance and associate it with the timeline. + try { + dispatch( + timelineActions.startTimelineSaving({ + id: TimelineId.active, + }) + ); + const response = await persistSavedSearch(savedSearch, { + onTitleDuplicate: () => {}, + copyOnSave: !savedSearchId, + }); + + if (!response || !response.id) { + throw new Error('Unknown Error occured'); + } - if (!savedSearchId) { + if (!savedSearchId) { + dispatch( + timelineActions.updateSavedSearchId({ + id: TimelineId.active, + savedSearchId: response.id, + }) + ); + // Also save the timeline, this will only happen once, in case there is no saved search id yet + dispatch(timelineActions.saveTimeline({ id: TimelineId.active, saveAsNew: false })); + } + } catch (err) { + addError(DISCOVER_SEARCH_SAVE_ERROR_TITLE, { + title: DISCOVER_SEARCH_SAVE_ERROR_TITLE, + toastMessage: String(err), + }); dispatch( - timelineActions.updateSavedSearchId({ + timelineActions.endTimelineSaving({ id: TimelineId.active, - savedSearchId: response.id, }) ); - // Also save the timeline, this will only happen once, in case there is no saved search id yet - dispatch(timelineActions.saveTimeline({ id: TimelineId.active })); } - } catch (err) { - addError(DISCOVER_SEARCH_SAVE_ERROR_TITLE, { - title: DISCOVER_SEARCH_SAVE_ERROR_TITLE, - toastMessage: String(err), - }); - } finally { - dispatch( - endTimelineSaving({ - id: timelineId, - }) - ); } }, [persistSavedSearch, savedSearchId, addError, dispatch, discoverDataService] ); + const initializeLocalSavedSearch = useCallback( + async (savedSearch: SavedSearch, timelineId: string) => { + dispatch( + timelineActions.initializeSavedSearch({ + id: TimelineId.active, + savedSearch, + }) + ); + }, + [dispatch] + ); + const actions = useMemo( () => ({ resetDiscoverAppState, restoreDiscoverAppStateFromSavedSearch, updateSavedSearch, + initializeLocalSavedSearch, getAppStateFromSavedSearch, getDefaultDiscoverAppState, }), @@ -238,6 +271,7 @@ export const useDiscoverInTimelineActions = ( resetDiscoverAppState, restoreDiscoverAppStateFromSavedSearch, updateSavedSearch, + initializeLocalSavedSearch, getAppStateFromSavedSearch, getDefaultDiscoverAppState, ] diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index 041ef14721b34..a1455f7e5cc09 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -7,14 +7,13 @@ import { noop, pick } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; -import type { DropResult } from '@hello-pangea/dnd'; +import type { DragStart, DropResult } from '@hello-pangea/dnd'; import { DragDropContext } from '@hello-pangea/dnd'; import { useDispatch } from 'react-redux'; import type { Dispatch } from 'redux'; import deepEqual from 'fast-deep-equal'; import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; -import type { BeforeCapture } from './drag_drop_context'; import type { BrowserFields } from '../../containers/source'; import { dragAndDropSelectors } from '../../store'; import { timelineSelectors } from '../../../timelines/store/timeline'; @@ -151,8 +150,9 @@ export const DragDropContextWrapperComponent: React.FC = ({ browserFields }, [activeTimelineDataProviders, browserFields, dataProviders, dispatch, onAddedToTimeline] ); + return ( - + {children} ); @@ -168,12 +168,12 @@ export const DragDropContextWrapper = React.memo( DragDropContextWrapper.displayName = 'DragDropContextWrapper'; -const onBeforeCapture = (before: BeforeCapture) => { - if (!draggableIsField(before)) { +const onBeforeDragStart = (start: DragStart) => { + if (!draggableIsField(start)) { document.body.classList.add(IS_DRAGGING_CLASS_NAME); } - if (draggableIsField(before)) { + if (draggableIsField(start)) { document.body.classList.add(IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME); } }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index edc72e92ff153..2718c3adf2012 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -26,7 +26,7 @@ import { mockAlertDetailsData } from './__mocks__'; import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { TimelineTabs } from '../../../../common/types/timeline'; import { useInvestigationTimeEnrichment } from '../../containers/cti/event_enrichment'; -import { useGetUserCasesPermissions, useKibana } from '../../lib/kibana'; +import { useKibana } from '../../lib/kibana'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; @@ -44,14 +44,8 @@ jest.mock('../../../timelines/components/timeline/body/renderers', () => { }); jest.mock('../../lib/kibana'); -const originalKibanaLib = jest.requireActual('../../lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); - jest.mock('../../containers/cti/event_enrichment'); jest.mock('../../../detection_engine/rule_management/logic/use_rule_with_fallback', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx index 2ef1277884c10..b9b132763d6f7 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.test.tsx @@ -12,7 +12,6 @@ import { TestProviders } from '../../../mock'; import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline'; import { useKibana as mockUseKibana } from '../../../lib/kibana/__mocks__'; -import { useGetUserCasesPermissions } from '../../../lib/kibana'; import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; import { licenseService } from '../../../hooks/use_license'; import { noCasesPermissions, readCasesPermissions } from '../../../../cases_test_utils'; @@ -20,12 +19,13 @@ import { Insights } from './insights'; import * as i18n from './translations'; const mockedUseKibana = mockUseKibana(); +const mockCanUseCases = jest.fn(); + jest.mock('../../../lib/kibana', () => { const original = jest.requireActual('../../../lib/kibana'); return { ...original, - useGetUserCasesPermissions: jest.fn(), useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), useKibana: () => ({ ...mockedUseKibana, @@ -35,12 +35,12 @@ jest.mock('../../../lib/kibana', () => { api: { getRelatedCases: jest.fn(), }, + helpers: { canUseCases: mockCanUseCases }, }, }, }), }; }); -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; jest.mock('../../../hooks/use_license', () => { const licenseServiceInstance = { @@ -94,7 +94,7 @@ const data: TimelineEventsDetailsItem[] = [ describe('Insights', () => { beforeEach(() => { - mockUseGetUserCasesPermissions.mockReturnValue(noCasesPermissions()); + mockCanUseCases.mockReturnValue(noCasesPermissions()); }); it('does not render when there is no content to show', () => { @@ -116,7 +116,7 @@ describe('Insights', () => { // It will show for all users that are able to read case data. // Enabling that permission, will show the case insight module which // is necessary to pass this test. - mockUseGetUserCasesPermissions.mockReturnValue(readCasesPermissions()); + mockCanUseCases.mockReturnValue(readCasesPermissions()); render( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx index e4e4f317467e2..60c89438aa12d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/insights.tsx @@ -11,12 +11,12 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { ALERT_SUPPRESSION_DOCS_COUNT } from '@kbn/rule-data-utils'; import { find } from 'lodash/fp'; +import { APP_ID } from '../../../../../common'; import * as i18n from './translations'; import type { BrowserFields } from '../../../containers/source'; import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline'; import { hasData } from './helpers'; -import { useGetUserCasesPermissions } from '../../../lib/kibana'; import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; import { useLicense } from '../../../hooks/use_license'; import { RelatedAlertsByProcessAncestry } from './related_alerts_by_process_ancestry'; @@ -24,6 +24,7 @@ import { RelatedCases } from './related_cases'; import { RelatedAlertsBySourceEvent } from './related_alerts_by_source_event'; import { RelatedAlertsBySession } from './related_alerts_by_session'; import { RelatedAlertsUpsell } from './related_alerts_upsell'; +import { useKibana } from '../../../lib/kibana'; const StyledInsightItem = euiStyled(EuiFlexItem)` border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; @@ -45,6 +46,7 @@ interface Props { */ export const Insights = React.memo( ({ browserFields, eventId, data, isReadOnly, scopeId }) => { + const { cases } = useKibana().services; const isRelatedAlertsByProcessAncestryEnabled = useIsExperimentalFeatureEnabled( 'insightsRelatedAlertsByProcessAncestry' ); @@ -83,7 +85,7 @@ export const Insights = React.memo( ); const hasAlertSuppressionField = hasData(alertSuppressionField); - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const hasCasesReadPermissions = userCasesPermissions.read; // Make sure that the alert has at least one of the associated fields diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_upsell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_upsell.tsx index 303e55ff66b97..10a9c872e3911 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_upsell.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_alerts_upsell.tsx @@ -6,17 +6,11 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiText } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { SubscriptionLink } from '@kbn/subscription-tracking'; -import type { SubscriptionContextData } from '@kbn/subscription-tracking'; import { INSIGHTS_UPSELL } from './translations'; - -const subscriptionContext: SubscriptionContextData = { - feature: 'alert-details-insights', - source: 'security__alert-details-flyout', -}; +import { useKibana } from '../../../lib/kibana'; const UpsellContainer = euiStyled.div` border: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; @@ -29,6 +23,7 @@ const StyledIcon = euiStyled(EuiIcon)` `; export const RelatedAlertsUpsell = React.memo(() => { + const { application } = useKibana().services; return ( @@ -37,13 +32,15 @@ export const RelatedAlertsUpsell = React.memo(() => { - {INSIGHTS_UPSELL} - + diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx index 8e6bc304e1a38..52a6d5eb1eb42 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx @@ -10,7 +10,6 @@ import React from 'react'; import { TestProviders } from '../../../mock'; import { useKibana as mockUseKibana } from '../../../lib/kibana/__mocks__'; -import { useGetUserCasesPermissions } from '../../../lib/kibana'; import { RelatedCases } from './related_cases'; import { noCasesPermissions, readCasesPermissions } from '../../../../cases_test_utils'; import { CASES_LOADING, CASES_COUNT } from './translations'; @@ -19,13 +18,14 @@ import { AlertsCasesTourSteps } from '../../guided_onboarding_tour/tour_config'; const mockedUseKibana = mockUseKibana(); const mockGetRelatedCases = jest.fn(); +const mockCanUseCases = jest.fn(); + jest.mock('../../guided_onboarding_tour'); jest.mock('../../../lib/kibana', () => { const original = jest.requireActual('../../../lib/kibana'); return { ...original, - useGetUserCasesPermissions: jest.fn(), useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), useKibana: () => ({ ...mockedUseKibana, @@ -35,6 +35,7 @@ jest.mock('../../../lib/kibana', () => { api: { getRelatedCases: mockGetRelatedCases, }, + helpers: { canUseCases: mockCanUseCases }, }, }, }), @@ -47,7 +48,7 @@ window.HTMLElement.prototype.scrollIntoView = scrollToMock; describe('Related Cases', () => { beforeEach(() => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(readCasesPermissions()); + mockCanUseCases.mockReturnValue(readCasesPermissions()); (useTourContext as jest.Mock).mockReturnValue({ activeStep: AlertsCasesTourSteps.viewCase, incrementStep: () => null, @@ -58,7 +59,7 @@ describe('Related Cases', () => { }); describe('When user does not have cases read permissions', () => { beforeEach(() => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(noCasesPermissions()); + mockCanUseCases.mockReturnValue(noCasesPermissions()); }); test('should not show related cases when user does not have permissions', async () => { await act(async () => { diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index cef3117cf28c5..903f1f7c548c9 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -22,7 +22,6 @@ import { useTimelineEvents } from './use_timelines_events'; import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; import type { UseFieldBrowserOptionsProps } from '../../../timelines/components/fields_browser'; -import { useGetUserCasesPermissions } from '../../lib/kibana'; import { TableId } from '@kbn/securitysolution-data-table'; import { mount } from 'enzyme'; @@ -38,13 +37,6 @@ jest.mock('react-redux', () => { }; }); -const originalKibanaLib = jest.requireActual('../../lib/kibana'); - -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); - jest.mock('./use_timelines_events'); jest.mock('../../utils/normalize_time_range'); diff --git a/x-pack/plugins/security_solution/public/common/components/exit_full_screen/index.tsx b/x-pack/plugins/security_solution/public/common/components/exit_full_screen/index.tsx index ee47fca53543b..d4652c4f5b629 100644 --- a/x-pack/plugins/security_solution/public/common/components/exit_full_screen/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exit_full_screen/index.tsx @@ -7,16 +7,11 @@ import { EuiButton, EuiWindowEvent } from '@elastic/eui'; import React, { useCallback } from 'react'; -import styled from 'styled-components'; import * as i18n from './translations'; export const EXIT_FULL_SCREEN_CLASS_NAME = 'exit-full-screen'; -const StyledEuiButton = styled(EuiButton)` - margin: ${({ theme }) => theme.eui.euiSizeS}; -`; - interface Props { fullScreen: boolean; setFullScreen: (fullScreen: boolean) => void; @@ -45,16 +40,17 @@ const ExitFullScreenComponent: React.FC = ({ fullScreen, setFullScreen }) return ( <> - {i18n.EXIT_FULL_SCREEN} - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap index 00732ec7b82e8..46c33d5102feb 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap @@ -37,7 +37,7 @@ exports[`HeaderSection it renders 1`] = ` >

    = ({ stackHeader, subtitle, title, - titleSize = 'm', + titleSize = 'l', toggleQuery, toggleStatus = true, tooltip, @@ -173,7 +173,6 @@ const HeaderSectionComponent: React.FC = ({ {title} {tooltip && ( <> - {' '} void; queryId: string; showInspectButton?: boolean; - title: string | React.ReactElement | React.ReactNode; + title?: string | React.ReactElement | React.ReactNode; } const InspectButtonComponent: React.FC = ({ @@ -80,9 +80,6 @@ const InspectButtonComponent: React.FC = ({ className={BUTTON_CLASS} aria-label={i18n.INSPECT} data-test-subj="inspect-empty-button" - color="text" - iconSide="left" - iconType="inspect" isDisabled={isButtonDisabled} isLoading={loading} onClick={handleClick} diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx index 968899a1e84d3..9d676016be252 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/index.test.tsx @@ -7,51 +7,22 @@ import React from 'react'; import { render } from '@testing-library/react'; import { LandingPageComponent } from '.'; -import { useKibana } from '../../lib/kibana'; -import { Router } from 'react-router-dom'; -import { createBrowserHistory } from 'history'; -import { TestProviders } from '../../mock/test_providers'; -jest.mock('../../lib/kibana', () => ({ - useKibana: jest.fn(), +const mockUseContractComponents = jest.fn(() => ({})); +jest.mock('../../hooks/use_contract_component', () => ({ + useContractComponents: () => mockUseContractComponents(), })); -jest.mock('react-use/lib/useObservable', () => jest.fn((component) => component)); - describe('LandingPageComponent', () => { - const mockGetComponent = jest.fn(); - const history = createBrowserHistory(); - const mockSecuritySolutionTemplateWrapper = jest - .fn() - .mockImplementation(({ children }) =>
    {children}
    ); - - const renderPage = () => - render( - - - , - { wrapper: TestProviders } - ); - - beforeAll(() => { - (useKibana as jest.Mock).mockReturnValue({ - services: { - securityLayout: { - getPluginWrapper: jest.fn().mockReturnValue(mockSecuritySolutionTemplateWrapper), - }, - getComponent$: mockGetComponent, - }, - }); - }); - beforeEach(() => { jest.clearAllMocks(); }); it('renders the get started component', () => { - mockGetComponent.mockReturnValue(
    ); - const { queryByTestId } = renderPage(); + const GetStarted = () =>
    ; + mockUseContractComponents.mockReturnValue({ GetStarted }); + const { queryByTestId } = render(); - expect(queryByTestId('get-started')).toBeInTheDocument(); + expect(queryByTestId('get-started-mock')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx index d8e1a9029e212..959827aec3ba4 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/index.tsx @@ -6,13 +6,11 @@ */ import React, { memo } from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import { useKibana } from '../../lib/kibana'; +import { useContractComponents } from '../../hooks/use_contract_component'; export const LandingPageComponent = memo(() => { - const { getComponent$ } = useKibana().services; - const GetStartedComponent = useObservable(getComponent$('getStarted')); - return <>{GetStartedComponent}; + const { GetStarted } = useContractComponents(); + return GetStarted ? : null; }); LandingPageComponent.displayName = 'LandingPageComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx index fe21d973c86b8..08818172bca5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx @@ -167,7 +167,6 @@ export const QueryBar = memo( savedQuery={savedQuery} displayStyle={displayStyle} isDisabled={isDisabled} - hideTextBasedRunQueryLabel /> ); } diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx index 7765c4dc0f79d..afe1d0f208765 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx @@ -11,21 +11,12 @@ import { TestProviders } from '../../mock'; import { TEST_ID, SessionsView, defaultSessionsFilter } from '.'; import type { EntityType } from '@kbn/timelines-plugin/common'; import type { SessionsComponentsProps } from './types'; -import { useGetUserCasesPermissions } from '../../lib/kibana'; import { TableId } from '@kbn/securitysolution-data-table'; import { licenseService } from '../../hooks/use_license'; import { mount } from 'enzyme'; import type { EventsViewerProps } from '../events_viewer'; jest.mock('../../lib/kibana'); - -const originalKibanaLib = jest.requireActual('../../lib/kibana'); - -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); - jest.mock('../../utils/normalize_time_range'); const startDate = '2022-03-22T22:10:56.794Z'; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/alerts_sourcerer.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/alerts_sourcerer.test.tsx new file mode 100644 index 0000000000000..da0bc5699882d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/alerts_sourcerer.test.tsx @@ -0,0 +1,139 @@ +/* + * Copyright 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 { Sourcerer } from '.'; +import { sourcererModel } from '../../store/sourcerer'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../mock'; +import { createStore } from '../../store'; +import { useSourcererDataView } from '../../containers/sourcerer'; +import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; + +const mockDispatch = jest.fn(); + +jest.mock('../../containers/sourcerer'); +jest.mock('../../containers/sourcerer/use_signal_helpers'); +const mockUseUpdateDataView = jest.fn().mockReturnValue(() => true); +jest.mock('./use_update_data_view', () => ({ + useUpdateDataView: () => mockUseUpdateDataView, +})); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + + return { + ...original, + toMountPoint: jest.fn(), + }; +}); + +const mockUpdateUrlParam = jest.fn(); +jest.mock('../../utils/global_query_string', () => { + const original = jest.requireActual('../../utils/global_query_string'); + + return { + ...original, + useUpdateUrlParam: () => mockUpdateUrlParam, + }; +}); + +let store: ReturnType; +const sourcererDataView = { + indicesExist: true, + loading: false, +}; +describe('sourcerer on alerts page or rules details page', () => { + const { storage } = createSecuritySolutionStorageMock(); + store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + const testProps = { + scope: sourcererModel.SourcererScopeName.detections, + }; + + const pollForSignalIndexMock = jest.fn(); + + beforeEach(async () => { + jest.clearAllMocks(); + + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + indicesExist: true, + }); + + render( + + + + ); + + fireEvent.click(screen.getByTestId('sourcerer-trigger')); + await waitFor(() => { + expect(screen.getByTestId('sourcerer-advanced-options-toggle')).toBeVisible(); + }); + fireEvent.click(screen.getByTestId('sourcerer-advanced-options-toggle')); + }); + + it('renders an alerts badge in sourcerer button', () => { + expect(screen.getByTestId('sourcerer-advanced-options-toggle')).toHaveTextContent( + /Advanced options/ + ); + }); + + it('renders a callout', () => { + expect(screen.getByTestId('sourcerer-callout')).toHaveTextContent( + 'Data view cannot be modified on this page' + ); + }); + + it('disable data view selector', () => { + expect(screen.getByTestId('sourcerer-select')).toBeDisabled(); + }); + + it('data view selector is default to Security Data View', () => { + expect(screen.getByTestId('sourcerer-select')).toHaveTextContent(/security data view/i); + }); + + it('renders an alert badge in data view selector', () => { + expect(screen.getByTestId('security-alerts-option-badge')).toHaveTextContent('Alerts'); + }); + + it('disable index pattern selector', () => { + expect(screen.getByTestId('sourcerer-combo-box')).toHaveAttribute('disabled'); + }); + + it('shows signal index as index pattern option', () => { + expect(screen.getByTestId('euiComboBoxPill')).toHaveTextContent('.siem-signals-spacename'); + }); + + it('does not render reset button', () => { + expect(screen.queryByTestId('sourcerer-reset')).toBeFalsy(); + }); + + it('does not render save button', () => { + expect(screen.queryByTestId('sourcerer-save')).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/helpers.tsx index 87874da00ced9..3556e32196573 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/helpers.tsx @@ -10,6 +10,7 @@ import type { EuiSuperSelectOption, EuiFormRowProps } from '@elastic/eui'; import { EuiIcon, EuiBadge, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; import styled, { css } from 'styled-components'; +import { euiThemeVars } from '@kbn/ui-theme'; import type { sourcererModel } from '../../store/sourcerer'; import * as i18n from './translations'; @@ -23,7 +24,7 @@ export const StyledFormRow = styled(EuiFormRow)` max-width: none; `; -export const StyledButton = styled(EuiButtonEmpty)` +export const StyledButtonEmpty = styled(EuiButtonEmpty)` &:enabled:focus, &:focus { background-color: transparent; @@ -43,7 +44,7 @@ export const PopoverContent = styled.div` `; export const StyledBadge = styled(EuiBadge)` - margin-left: 8px; + margin-left: ${euiThemeVars.euiSizeXS}; &, .euiBadge__text { cursor: pointer; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx index ebda7e6748ebd..f91ebef48d7a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx @@ -8,9 +8,8 @@ import React from 'react'; import type { ReactWrapper } from 'enzyme'; import { mount } from 'enzyme'; -import { cloneDeep } from 'lodash'; -import { initialSourcererState, SourcererScopeName } from '../../store/sourcerer/model'; +import { SourcererScopeName } from '../../store/sourcerer/model'; import { Sourcerer } from '.'; import { sourcererActions, sourcererModel } from '../../store/sourcerer'; import { @@ -22,11 +21,9 @@ import { } from '../../mock'; import { createStore } from '../../store'; import type { EuiSuperSelectOption } from '@elastic/eui/src/components/form/super_select/super_select_control'; -import { waitFor } from '@testing-library/react'; +import { fireEvent, waitFor, render } from '@testing-library/react'; import { useSourcererDataView } from '../../containers/sourcerer'; import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'; -import { TimelineId } from '../../../../common/types/timeline'; -import { TimelineType } from '../../../../common/api/timeline'; import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; import { sortWithExcludesAtEnd } from '../../../../common/utils/sourcerer'; @@ -93,6 +90,7 @@ const sourcererDataView = { describe('Sourcerer component', () => { const { storage } = createSecuritySolutionStorageMock(); const pollForSignalIndexMock = jest.fn(); + let wrapper: ReactWrapper; beforeEach(() => { jest.clearAllMocks(); store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); @@ -100,8 +98,12 @@ describe('Sourcerer component', () => { (useSignalHelpers as jest.Mock).mockReturnValue({ signalIndexNeedsInit: false }); }); + afterEach(() => { + if (wrapper && wrapper.exists()) wrapper.unmount(); + }); + it('renders data view title', () => { - const wrapper = mount( + wrapper = mount( @@ -117,7 +119,7 @@ describe('Sourcerer component', () => { ...defaultProps, showAlertsOnlyCheckbox: true, }; - const wrapper = mount( + wrapper = mount( @@ -129,7 +131,7 @@ describe('Sourcerer component', () => { }); it('renders tooltip', () => { - const wrapper = mount( + wrapper = mount( @@ -140,7 +142,7 @@ describe('Sourcerer component', () => { }); it('renders popover button inside tooltip', () => { - const wrapper = mount( + wrapper = mount( @@ -156,7 +158,7 @@ describe('Sourcerer component', () => { // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects it('Mounts with all options selected', () => { - const wrapper = mount( + wrapper = mount( @@ -206,7 +208,7 @@ describe('Sourcerer component', () => { kibanaObservable, storage ); - const wrapper = mount( + wrapper = mount( @@ -256,7 +258,7 @@ describe('Sourcerer component', () => { kibanaObservable, storage ); - const wrapper = mount( + wrapper = mount( @@ -305,7 +307,7 @@ describe('Sourcerer component', () => { }; store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const wrapper = mount( + wrapper = mount( @@ -318,7 +320,7 @@ describe('Sourcerer component', () => { optionsSelected: true, }); }); - it('Mounts with multiple options selected - timeline', () => { + it('Mounts with multiple options selected - timeline', async () => { const state2 = { ...mockGlobalState, sourcerer: { @@ -350,17 +352,22 @@ describe('Sourcerer component', () => { }; store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const wrapper = mount( + const { getByTestId, queryByTitle, queryAllByTestId } = render( ); - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="comboBoxInput"]`).first().simulate('click'); - expect(checkOptionsAndSelections(wrapper, patternList.slice(0, 2))).toEqual({ - // should show every option except fakebeat-* - availableOptionCount: title.split(',').length - 2, - optionsSelected: true, + + fireEvent.click(getByTestId('timeline-sourcerer-trigger')); + await waitFor(() => { + for (const pattern of patternList.slice(0, 2)) { + expect(queryByTitle(pattern)).toBeInTheDocument(); + } + }); + + fireEvent.click(getByTestId('comboBoxInput')); + await waitFor(() => { + expect(queryAllByTestId('sourcerer-combo-option')).toHaveLength(title.split(',').length - 2); }); }); it('onSave dispatches setSelectedDataView', async () => { @@ -392,7 +399,7 @@ describe('Sourcerer component', () => { kibanaObservable, storage ); - const wrapper = mount( + wrapper = mount( @@ -450,7 +457,7 @@ describe('Sourcerer component', () => { storage ); - const wrapper = mount( + wrapper = mount( @@ -464,7 +471,7 @@ describe('Sourcerer component', () => { }); it('resets to default index pattern', async () => { - const wrapper = mount( + wrapper = mount( @@ -517,7 +524,7 @@ describe('Sourcerer component', () => { kibanaObservable, storage ); - const wrapper = mount( + wrapper = mount( @@ -526,7 +533,14 @@ describe('Sourcerer component', () => { wrapper.find('[data-test-subj="comboBoxClearButton"]').first().simulate('click'); expect(wrapper.find('[data-test-subj="sourcerer-save"]').first().prop('disabled')).toBeTruthy(); }); - it('Does display signals index on timeline sourcerer', () => { + it('Does display signals index on timeline sourcerer', async () => { + /* + * Since both enzyme and RTL share JSDOM when running these tests, + * and enzyme does not clears jsdom after each test, because of this + * `screen` of RTL does not work as expect, please avoid using screen + * till all the tests have been converted to RTL + * + * */ const state2 = { ...mockGlobalState, sourcerer: { @@ -559,16 +573,20 @@ describe('Sourcerer component', () => { }; store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const wrapper = mount( + const el = render( ); - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="comboBoxToggleListButton"]`).first().simulate('click'); - expect(wrapper.find(`[data-test-subj="sourcerer-combo-option"]`).at(0).text()).toEqual( - mockGlobalState.sourcerer.signalIndexName - ); + + fireEvent.click(el.getByTestId('timeline-sourcerer-trigger')); + fireEvent.click(el.getByTestId('comboBoxToggleListButton')); + + await waitFor(() => { + expect(el.queryAllByTestId('sourcerer-combo-option')[0].textContent).toBe( + mockGlobalState.sourcerer.signalIndexName + ); + }); }); it('Does not display signals index on default sourcerer', () => { const state2 = { @@ -603,7 +621,7 @@ describe('Sourcerer component', () => { }; store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const wrapper = mount( + wrapper = mount( @@ -677,617 +695,3 @@ describe('Sourcerer component', () => { expect(pollForSignalIndexMock).toHaveBeenCalledTimes(1); }); }); - -describe('sourcerer on alerts page or rules details page', () => { - let wrapper: ReactWrapper; - const { storage } = createSecuritySolutionStorageMock(); - store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const testProps = { - scope: sourcererModel.SourcererScopeName.detections, - }; - - beforeAll(() => { - wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="sourcerer-advanced-options-toggle"]`).first().simulate('click'); - }); - - it('renders an alerts badge in sourcerer button', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-alerts-badge"]`).first().text()).toEqual( - 'Alerts' - ); - }); - - it('renders a callout', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-callout"]`).first().text()).toEqual( - 'Data view cannot be modified on this page' - ); - }); - - it('disable data view selector', () => { - expect( - wrapper.find(`[data-test-subj="sourcerer-select"]`).first().prop('disabled') - ).toBeTruthy(); - }); - - it('data view selector is default to Security Data View', () => { - expect( - wrapper.find(`[data-test-subj="sourcerer-select"]`).first().prop('valueOfSelected') - ).toEqual('security-solution'); - }); - - it('renders an alert badge in data view selector', () => { - expect(wrapper.find(`[data-test-subj="security-alerts-option-badge"]`).first().text()).toEqual( - 'Alerts' - ); - }); - - it('disable index pattern selector', () => { - expect( - wrapper.find(`[data-test-subj="sourcerer-combo-box"]`).first().prop('disabled') - ).toBeTruthy(); - }); - - it('shows signal index as index pattern option', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-combo-box"]`).first().prop('options')).toEqual([ - { disabled: false, label: '.siem-signals-spacename', value: '.siem-signals-spacename' }, - ]); - }); - - it('does not render reset button', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-reset"]`).exists()).toBeFalsy(); - }); - - it('does not render save button', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-save"]`).exists()).toBeFalsy(); - }); -}); - -describe('timeline sourcerer', () => { - let wrapper: ReactWrapper; - const { storage } = createSecuritySolutionStorageMock(); - store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - const testProps = { - scope: sourcererModel.SourcererScopeName.timeline, - }; - - beforeAll(() => { - (useSourcererDataView as jest.Mock).mockReturnValue(sourcererDataView); - wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - wrapper - .find( - `[data-test-subj="timeline-sourcerer-popover"] [data-test-subj="sourcerer-advanced-options-toggle"]` - ) - .first() - .simulate('click'); - }); - - it('renders "alerts only" checkbox, unchecked', () => { - wrapper - .find( - `[data-test-subj="timeline-sourcerer-popover"] [data-test-subj="sourcerer-alert-only-checkbox"]` - ) - .first() - .simulate('click'); - expect(wrapper.find(`[data-test-subj="sourcerer-alert-only-checkbox"]`).first().text()).toEqual( - 'Show only detection alerts' - ); - expect( - wrapper.find(`[data-test-subj="sourcerer-alert-only-checkbox"] input`).first().prop('checked') - ).toEqual(false); - }); - - it('data view selector is enabled', () => { - expect( - wrapper - .find(`[data-test-subj="timeline-sourcerer-popover"] [data-test-subj="sourcerer-select"]`) - .first() - .prop('disabled') - ).toBeFalsy(); - }); - - it('data view selector is default to Security Default Data View', () => { - expect( - wrapper - .find(`[data-test-subj="timeline-sourcerer-popover"] [data-test-subj="sourcerer-select"]`) - .first() - .prop('valueOfSelected') - ).toEqual('security-solution'); - }); - - it('index pattern selector is enabled', () => { - expect( - wrapper - .find( - `[data-test-subj="timeline-sourcerer-popover"] [data-test-subj="sourcerer-combo-box"]` - ) - .first() - .prop('disabled') - ).toBeFalsy(); - }); - - it('render reset button', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-reset"]`).exists()).toBeTruthy(); - }); - - it('render save button', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-save"]`).exists()).toBeTruthy(); - }); - - it('Checks box when only alerts index is selected in timeline', () => { - const state2 = { - ...mockGlobalState, - sourcerer: { - ...mockGlobalState.sourcerer, - sourcererScopes: { - ...mockGlobalState.sourcerer.sourcererScopes, - [SourcererScopeName.timeline]: { - ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], - loading: false, - selectedDataViewId: id, - selectedPatterns: [`${mockGlobalState.sourcerer.signalIndexName}`], - }, - }, - }, - }; - - store = createStore( - state2, - SUB_PLUGINS_REDUCER, - - kibanaObservable, - storage - ); - - wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - expect( - wrapper.find(`[data-test-subj="sourcerer-alert-only-checkbox"] input`).first().prop('checked') - ).toEqual(true); - }); -}); - -describe('Sourcerer integration tests', () => { - const state = { - ...mockGlobalState, - sourcerer: { - ...mockGlobalState.sourcerer, - kibanaDataViews: [ - mockGlobalState.sourcerer.defaultDataView, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '1234', - title: 'fakebeat-*,neatbeat-*', - patternList: ['fakebeat-*'], - }, - ], - sourcererScopes: { - ...mockGlobalState.sourcerer.sourcererScopes, - [SourcererScopeName.default]: { - ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default], - loading: false, - selectedDataViewId: id, - selectedPatterns: patternListNoSignals.slice(0, 2), - }, - }, - }, - }; - - const { storage } = createSecuritySolutionStorageMock(); - - beforeEach(() => { - (useSourcererDataView as jest.Mock).mockReturnValue(sourcererDataView); - store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - jest.clearAllMocks(); - }); - - it('Selects a different index pattern', async () => { - const wrapper = mount( - - - - ); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - wrapper.find(`button[data-test-subj="sourcerer-select"]`).first().simulate('click'); - - wrapper.find(`[data-test-subj="dataView-option-super"]`).first().simulate('click'); - expect(checkOptionsAndSelections(wrapper, ['fakebeat-*'])).toEqual({ - availableOptionCount: 0, - optionsSelected: true, - }); - wrapper.find(`button[data-test-subj="sourcerer-save"]`).first().simulate('click'); - - expect(mockDispatch).toHaveBeenCalledWith( - sourcererActions.setSelectedDataView({ - id: SourcererScopeName.default, - selectedDataViewId: '1234', - selectedPatterns: ['fakebeat-*'], - }) - ); - }); -}); - -describe('No data', () => { - const mockNoIndicesState = { - ...mockGlobalState, - sourcerer: { - ...initialSourcererState, - }, - }; - - const { storage } = createSecuritySolutionStorageMock(); - - beforeEach(() => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - indicesExist: false, - }); - store = createStore(mockNoIndicesState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - jest.clearAllMocks(); - }); - - test('Hide sourcerer - default ', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="sourcerer-trigger"]`).exists()).toEqual(false); - }); - test('Hide sourcerer - detections ', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="sourcerer-trigger"]`).exists()).toEqual(false); - }); - test('Hide sourcerer - timeline ', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).exists()).toEqual(true); - }); -}); - -describe('Update available', () => { - const { storage } = createSecuritySolutionStorageMock(); - const state2 = { - ...mockGlobalState, - sourcerer: { - ...mockGlobalState.sourcerer, - kibanaDataViews: [ - mockGlobalState.sourcerer.defaultDataView, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '1234', - title: 'auditbeat-*', - patternList: ['auditbeat-*'], - }, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '12347', - title: 'packetbeat-*', - patternList: ['packetbeat-*'], - }, - ], - sourcererScopes: { - ...mockGlobalState.sourcerer.sourcererScopes, - [SourcererScopeName.timeline]: { - ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], - loading: false, - patternList, - selectedDataViewId: null, - selectedPatterns: ['myFakebeat-*'], - missingPatterns: ['myFakebeat-*'], - }, - }, - }, - }; - - let wrapper: ReactWrapper; - - beforeEach(() => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - activePatterns: ['myFakebeat-*'], - }); - store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - wrapper = mount( - - - - ); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('Show Update available label', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-badge"]`).exists()).toBeTruthy(); - }); - - test('Show correct tooltip', () => { - expect(wrapper.find(`[data-test-subj="sourcerer-tooltip"]`).prop('content')).toEqual( - 'myFakebeat-*' - ); - }); - - test('Show UpdateDefaultDataViewModal', () => { - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`UpdateDefaultDataViewModal`).prop('isShowing')).toEqual(true); - }); - - test('Show UpdateDefaultDataViewModal Callout', () => { - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-callout"]`).first().text()).toEqual( - 'This timeline uses a legacy data view selector' - ); - - expect( - wrapper.find(`[data-test-subj="sourcerer-current-patterns-message"]`).first().text() - ).toEqual('The active index patterns in this timeline are: myFakebeat-*'); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-message"]`).first().text()).toEqual( - "We have preserved your timeline by creating a temporary data view. If you'd like to modify your data, we can recreate your temporary data view with the new data view selector. You can also manually select a data view here." - ); - }); - - test('Show Add index pattern in UpdateDefaultDataViewModal', () => { - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`button[data-test-subj="sourcerer-update-data-view"]`).text()).toEqual( - 'Add index pattern' - ); - }); - - test('Set all the index patterns from legacy timeline to sourcerer, after clicking on "Add index pattern"', async () => { - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-update-data-view"]`).simulate('click'); - - await waitFor(() => wrapper.update()); - expect(mockDispatch).toHaveBeenCalledWith( - sourcererActions.setSelectedDataView({ - id: SourcererScopeName.timeline, - selectedDataViewId: 'security-solution', - selectedPatterns: ['myFakebeat-*'], - shouldValidateSelectedPatterns: false, - }) - ); - }); -}); - -describe('Update available for timeline template', () => { - const { storage } = createSecuritySolutionStorageMock(); - const state2 = { - ...mockGlobalState, - timeline: { - ...mockGlobalState.timeline, - timelineById: { - ...mockGlobalState.timeline.timelineById, - [TimelineId.active]: { - ...mockGlobalState.timeline.timelineById.test, - timelineType: TimelineType.template, - }, - }, - }, - sourcerer: { - ...mockGlobalState.sourcerer, - kibanaDataViews: [ - mockGlobalState.sourcerer.defaultDataView, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '1234', - title: 'auditbeat-*', - patternList: ['auditbeat-*'], - }, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '12347', - title: 'packetbeat-*', - patternList: ['packetbeat-*'], - }, - ], - sourcererScopes: { - ...mockGlobalState.sourcerer.sourcererScopes, - [SourcererScopeName.timeline]: { - ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], - loading: false, - patternList, - selectedDataViewId: null, - selectedPatterns: ['myFakebeat-*'], - missingPatterns: ['myFakebeat-*'], - }, - }, - }, - }; - - let wrapper: ReactWrapper; - - beforeEach(() => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - activePatterns: ['myFakebeat-*'], - }); - store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - wrapper = mount( - - - - ); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('Show UpdateDefaultDataViewModal CallOut', () => { - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-callout"]`).first().text()).toEqual( - 'This timeline template uses a legacy data view selector' - ); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-message"]`).first().text()).toEqual( - "We have preserved your timeline template by creating a temporary data view. If you'd like to modify your data, we can recreate your temporary data view with the new data view selector. You can also manually select a data view here." - ); - }); -}); - -describe('Missing index patterns', () => { - const { storage } = createSecuritySolutionStorageMock(); - const state2 = { - ...mockGlobalState, - timeline: { - ...mockGlobalState.timeline, - timelineById: { - ...mockGlobalState.timeline.timelineById, - [TimelineId.active]: { - ...mockGlobalState.timeline.timelineById.test, - timelineType: TimelineType.template, - }, - }, - }, - sourcerer: { - ...mockGlobalState.sourcerer, - kibanaDataViews: [ - mockGlobalState.sourcerer.defaultDataView, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '1234', - title: 'auditbeat-*', - patternList: ['auditbeat-*'], - }, - { - ...mockGlobalState.sourcerer.defaultDataView, - id: '12347', - title: 'packetbeat-*', - patternList: ['packetbeat-*'], - }, - ], - sourcererScopes: { - ...mockGlobalState.sourcerer.sourcererScopes, - [SourcererScopeName.timeline]: { - ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], - loading: false, - patternList, - selectedDataViewId: 'fake-data-view-id', - selectedPatterns: ['myFakebeat-*'], - missingPatterns: ['myFakebeat-*'], - }, - }, - }, - }; - - let wrapper: ReactWrapper; - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('Show UpdateDefaultDataViewModal CallOut for timeline', () => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - activePatterns: ['myFakebeat-*'], - }); - const state3 = cloneDeep(state2); - state3.timeline.timelineById[TimelineId.active].timelineType = TimelineType.default; - store = createStore(state3, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - wrapper = mount( - - - - ); - - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-callout"]`).first().text()).toEqual( - 'This timeline is out of date with the Security Data View' - ); - - expect( - wrapper.find(`[data-test-subj="sourcerer-current-patterns-message"]`).first().text() - ).toEqual('The active index patterns in this timeline are: myFakebeat-*'); - - expect( - wrapper.find(`[data-test-subj="sourcerer-missing-patterns-callout"]`).first().text() - ).toEqual('Security Data View is missing the following index patterns: myFakebeat-*'); - - expect( - wrapper.find(`[data-test-subj="sourcerer-missing-patterns-message"]`).first().text() - ).toEqual( - "We have preserved your timeline by creating a temporary data view. If you'd like to modify your data, we can add the missing index patterns to the Security Data View. You can also manually select a data view here." - ); - }); - - test('Show UpdateDefaultDataViewModal CallOut for timeline template', () => { - (useSourcererDataView as jest.Mock).mockReturnValue({ - ...sourcererDataView, - activePatterns: ['myFakebeat-*'], - }); - store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - - wrapper = mount( - - - - ); - - wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).first().simulate('click'); - - wrapper.find(`button[data-test-subj="sourcerer-deprecated-update"]`).first().simulate('click'); - - expect(wrapper.find(`[data-test-subj="sourcerer-deprecated-callout"]`).first().text()).toEqual( - 'This timeline template is out of date with the Security Data View' - ); - - expect( - wrapper.find(`[data-test-subj="sourcerer-current-patterns-message"]`).first().text() - ).toEqual('The active index patterns in this timeline template are: myFakebeat-*'); - - expect( - wrapper.find(`[data-test-subj="sourcerer-missing-patterns-callout"]`).first().text() - ).toEqual('Security Data View is missing the following index patterns: myFakebeat-*'); - - expect( - wrapper.find(`[data-test-subj="sourcerer-missing-patterns-message"]`).first().text() - ).toEqual( - "We have preserved your timeline template by creating a temporary data view. If you'd like to modify your data, we can add the missing index patterns to the Security Data View. You can also manually select a data view here." - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx index a0bb1f3f27ff4..5a2f3050c1590 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx @@ -25,7 +25,7 @@ import { useDeepEqualSelector } from '../../hooks/use_selector'; import type { SourcererUrlState } from '../../store/sourcerer/model'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { usePickIndexPatterns } from './use_pick_index_patterns'; -import { FormRow, PopoverContent, StyledButton, StyledFormRow } from './helpers'; +import { FormRow, PopoverContent, StyledButtonEmpty, StyledFormRow } from './helpers'; import { TemporarySourcerer } from './temporary'; import { useSourcererDataView } from '../../containers/sourcerer'; import { useUpdateDataView } from './use_update_data_view'; @@ -338,14 +338,14 @@ export const Sourcerer = React.memo(({ scope: scopeId } )} - {i18n.INDEX_PATTERNS_ADVANCED_OPTIONS_TITLE} - + {expandAdvancedOptions && } true); +jest.mock('./use_update_data_view', () => ({ + useUpdateDataView: () => mockUseUpdateDataView, +})); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + + return { + ...original, + toMountPoint: jest.fn(), + }; +}); + +const mockUpdateUrlParam = jest.fn(); +jest.mock('../../utils/global_query_string', () => { + const original = jest.requireActual('../../utils/global_query_string'); + + return { + ...original, + useUpdateUrlParam: () => mockUpdateUrlParam, + }; +}); + +const defaultProps = { + scope: sourcererModel.SourcererScopeName.default, +}; + +const checkOptionsAndSelections = (wrapper: ReactWrapper, patterns: string[]) => ({ + availableOptionCount: + wrapper.find('List').length > 0 ? wrapper.find('List').prop('itemCount') : 0, + optionsSelected: patterns.every((pattern) => + wrapper.find(`[data-test-subj="sourcerer-combo-box"] span[title="${pattern}"]`).first().exists() + ), +}); + +const { id, patternList } = mockGlobalState.sourcerer.defaultDataView; + +const patternListNoSignals = sortWithExcludesAtEnd( + patternList.filter((p) => p !== mockGlobalState.sourcerer.signalIndexName) +); +let store: ReturnType; +const sourcererDataView = { + indicesExist: true, + loading: false, +}; + +describe('No data', () => { + const mockNoIndicesState = { + ...mockGlobalState, + sourcerer: { + ...initialSourcererState, + }, + }; + + const { storage } = createSecuritySolutionStorageMock(); + const pollForSignalIndexMock = jest.fn(); + + beforeEach(() => { + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + indicesExist: false, + }); + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + store = createStore(mockNoIndicesState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + jest.clearAllMocks(); + }); + + test('Hide sourcerer - default ', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="sourcerer-trigger"]`).exists()).toEqual(false); + }); + test('Hide sourcerer - detections ', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="sourcerer-trigger"]`).exists()).toEqual(false); + }); + test('Hide sourcerer - timeline ', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="timeline-sourcerer-trigger"]`).exists()).toEqual(true); + }); +}); + +describe('Update available', () => { + const { storage } = createSecuritySolutionStorageMock(); + const state2 = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + kibanaDataViews: [ + mockGlobalState.sourcerer.defaultDataView, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '1234', + title: 'auditbeat-*', + patternList: ['auditbeat-*'], + }, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '12347', + title: 'packetbeat-*', + patternList: ['packetbeat-*'], + }, + ], + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.timeline]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], + loading: false, + patternList, + selectedDataViewId: null, + selectedPatterns: ['myFakebeat-*'], + missingPatterns: ['myFakebeat-*'], + }, + }, + }, + }; + + const pollForSignalIndexMock = jest.fn(); + beforeEach(() => { + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + render( + + + + ); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Show Update available label', () => { + expect(screen.getByTestId('sourcerer-deprecated-badge')).toBeInTheDocument(); + }); + + test('Show correct tooltip', async () => { + fireEvent.mouseOver(screen.getByTestId('timeline-sourcerer-trigger')); + await waitFor(() => { + expect(screen.getByTestId('sourcerer-tooltip').textContent).toBe('myFakebeat-*'); + }); + }); + + test('Show UpdateDefaultDataViewModal', () => { + fireEvent.click(screen.queryAllByTestId('timeline-sourcerer-trigger')[0]); + + fireEvent.click(screen.queryAllByTestId('sourcerer-deprecated-update')[0]); + + expect(screen.getByTestId('sourcerer-update-data-view-modal')).toBeVisible(); + }); + + test('Show UpdateDefaultDataViewModal Callout', () => { + fireEvent.click(screen.queryAllByTestId('timeline-sourcerer-trigger')[0]); + + fireEvent.click(screen.queryAllByTestId('sourcerer-deprecated-update')[0]); + + expect(screen.queryAllByTestId('sourcerer-deprecated-callout')[0].textContent).toBe( + 'This timeline uses a legacy data view selector' + ); + + expect(screen.queryAllByTestId('sourcerer-current-patterns-message')[0].textContent).toBe( + 'The active index patterns in this timeline are: myFakebeat-*' + ); + + expect(screen.queryAllByTestId('sourcerer-deprecated-message')[0].textContent).toBe( + "We have preserved your timeline by creating a temporary data view. If you'd like to modify your data, we can recreate your temporary data view with the new data view selector. You can also manually select a data view here." + ); + }); + + test('Show Add index pattern in UpdateDefaultDataViewModal', () => { + fireEvent.click(screen.queryAllByTestId('timeline-sourcerer-trigger')[0]); + + fireEvent.click(screen.queryAllByTestId('sourcerer-deprecated-update')[0]); + + expect(screen.queryAllByTestId('sourcerer-update-data-view')[0].textContent).toBe( + 'Add index pattern' + ); + }); + + test('Set all the index patterns from legacy timeline to sourcerer, after clicking on "Add index pattern"', async () => { + fireEvent.click(screen.queryAllByTestId('timeline-sourcerer-trigger')[0]); + + fireEvent.click(screen.queryAllByTestId('sourcerer-deprecated-update')[0]); + + fireEvent.click(screen.queryAllByTestId('sourcerer-update-data-view')[0]); + + await waitFor(() => { + expect(mockDispatch).toHaveBeenCalledWith( + sourcererActions.setSelectedDataView({ + id: SourcererScopeName.timeline, + selectedDataViewId: 'security-solution', + selectedPatterns: ['myFakebeat-*'], + shouldValidateSelectedPatterns: false, + }) + ); + }); + }); +}); + +describe('Update available for timeline template', () => { + const { storage } = createSecuritySolutionStorageMock(); + const state2 = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: { + ...mockGlobalState.timeline.timelineById.test, + timelineType: TimelineType.template, + }, + }, + }, + sourcerer: { + ...mockGlobalState.sourcerer, + kibanaDataViews: [ + mockGlobalState.sourcerer.defaultDataView, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '1234', + title: 'auditbeat-*', + patternList: ['auditbeat-*'], + }, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '12347', + title: 'packetbeat-*', + patternList: ['packetbeat-*'], + }, + ], + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.timeline]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], + loading: false, + patternList, + selectedDataViewId: null, + selectedPatterns: ['myFakebeat-*'], + missingPatterns: ['myFakebeat-*'], + }, + }, + }, + }; + + beforeEach(() => { + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + render( + + + + ); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Show UpdateDefaultDataViewModal CallOut', () => { + fireEvent.click(screen.getByTestId('timeline-sourcerer-trigger')); + fireEvent.click(screen.getByTestId('sourcerer-deprecated-update')); + + expect(screen.getByTestId('sourcerer-deprecated-callout')).toHaveTextContent( + 'This timeline template uses a legacy data view selector' + ); + + expect(screen.getByTestId('sourcerer-deprecated-message')).toHaveTextContent( + "We have preserved your timeline template by creating a temporary data view. If you'd like to modify your data, we can recreate your temporary data view with the new data view selector. You can also manually select a data view here." + ); + }); +}); + +describe('Missing index patterns', () => { + const { storage } = createSecuritySolutionStorageMock(); + const state2 = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: { + ...mockGlobalState.timeline.timelineById.test, + timelineType: TimelineType.template, + }, + }, + }, + sourcerer: { + ...mockGlobalState.sourcerer, + kibanaDataViews: [ + mockGlobalState.sourcerer.defaultDataView, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '1234', + title: 'auditbeat-*', + patternList: ['auditbeat-*'], + }, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '12347', + title: 'packetbeat-*', + patternList: ['packetbeat-*'], + }, + ], + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.timeline]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], + loading: false, + patternList, + selectedDataViewId: 'fake-data-view-id', + selectedPatterns: ['myFakebeat-*'], + missingPatterns: ['myFakebeat-*'], + }, + }, + }, + }; + + beforeEach(() => { + const pollForSignalIndexMock = jest.fn(); + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Show UpdateDefaultDataViewModal CallOut for timeline', async () => { + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + const state3 = cloneDeep(state2); + state3.timeline.timelineById[TimelineId.active].timelineType = TimelineType.default; + store = createStore(state3, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + render( + + + + ); + + fireEvent.click(screen.getByTestId('timeline-sourcerer-trigger')); + + fireEvent.click(screen.getByTestId('sourcerer-deprecated-update')); + + expect(screen.getByTestId('sourcerer-deprecated-callout').textContent).toBe( + 'This timeline is out of date with the Security Data View' + ); + expect(screen.getByTestId('sourcerer-current-patterns-message').textContent).toBe( + 'The active index patterns in this timeline are: myFakebeat-*' + ); + expect(screen.queryAllByTestId('sourcerer-missing-patterns-callout')[0].textContent).toBe( + 'Security Data View is missing the following index patterns: myFakebeat-*' + ); + expect(screen.queryAllByTestId('sourcerer-missing-patterns-message')[0].textContent).toBe( + "We have preserved your timeline by creating a temporary data view. If you'd like to modify your data, we can add the missing index patterns to the Security Data View. You can also manually select a data view here." + ); + }); + + test('Show UpdateDefaultDataViewModal CallOut for timeline template', async () => { + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + render( + + + + ); + + fireEvent.click(screen.getByTestId('timeline-sourcerer-trigger')); + + fireEvent.click(screen.getByTestId('sourcerer-deprecated-update')); + + await waitFor(() => { + expect(screen.queryAllByTestId('sourcerer-deprecated-callout')[0].textContent).toBe( + 'This timeline template is out of date with the Security Data View' + ); + + expect(screen.queryAllByTestId('sourcerer-current-patterns-message')[0].textContent).toBe( + 'The active index patterns in this timeline template are: myFakebeat-*' + ); + + expect(screen.queryAllByTestId('sourcerer-missing-patterns-callout')[0].textContent).toBe( + 'Security Data View is missing the following index patterns: myFakebeat-*' + ); + + expect(screen.queryAllByTestId('sourcerer-missing-patterns-message')[0].textContent).toBe( + "We have preserved your timeline template by creating a temporary data view. If you'd like to modify your data, we can add the missing index patterns to the Security Data View. You can also manually select a data view here." + ); + }); + }); +}); + +describe('Sourcerer integration tests', () => { + const state = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + kibanaDataViews: [ + mockGlobalState.sourcerer.defaultDataView, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '1234', + title: 'fakebeat-*,neatbeat-*', + patternList: ['fakebeat-*'], + }, + ], + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.default]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default], + loading: false, + selectedDataViewId: id, + selectedPatterns: patternListNoSignals.slice(0, 2), + }, + }, + }, + }; + + const { storage } = createSecuritySolutionStorageMock(); + + beforeEach(() => { + const pollForSignalIndexMock = jest.fn(); + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + jest.clearAllMocks(); + }); + + it('Selects a different index pattern', async () => { + const wrapper = mount( + + + + ); + wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); + wrapper.find(`button[data-test-subj="sourcerer-select"]`).first().simulate('click'); + + wrapper.find(`[data-test-subj="dataView-option-super"]`).first().simulate('click'); + expect(checkOptionsAndSelections(wrapper, ['fakebeat-*'])).toEqual({ + availableOptionCount: 0, + optionsSelected: true, + }); + wrapper.find(`button[data-test-subj="sourcerer-save"]`).first().simulate('click'); + + expect(mockDispatch).toHaveBeenCalledWith( + sourcererActions.setSelectedDataView({ + id: SourcererScopeName.default, + selectedDataViewId: '1234', + selectedPatterns: ['fakebeat-*'], + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/sourcerer_integration.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/sourcerer_integration.test.tsx new file mode 100644 index 0000000000000..33eba9dac6b95 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/sourcerer_integration.test.tsx @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { ReactWrapper } from 'enzyme'; +import { mount } from 'enzyme'; + +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { Sourcerer } from '.'; +import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'; +import { sourcererActions, sourcererModel } from '../../store/sourcerer'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../mock'; +import { createStore } from '../../store'; +import { sortWithExcludesAtEnd } from '../../../../common/utils/sourcerer'; +import { useSourcererDataView } from '../../containers/sourcerer'; + +const mockDispatch = jest.fn(); + +jest.mock('../../containers/sourcerer'); +jest.mock('../../containers/sourcerer/use_signal_helpers'); +const mockUseUpdateDataView = jest.fn().mockReturnValue(() => true); +jest.mock('./use_update_data_view', () => ({ + useUpdateDataView: () => mockUseUpdateDataView, +})); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + + return { + ...original, + toMountPoint: jest.fn(), + }; +}); + +const mockUpdateUrlParam = jest.fn(); +jest.mock('../../utils/global_query_string', () => { + const original = jest.requireActual('../../utils/global_query_string'); + + return { + ...original, + useUpdateUrlParam: () => mockUpdateUrlParam, + }; +}); + +const defaultProps = { + scope: sourcererModel.SourcererScopeName.default, +}; + +const checkOptionsAndSelections = (wrapper: ReactWrapper, patterns: string[]) => ({ + availableOptionCount: + wrapper.find('List').length > 0 ? wrapper.find('List').prop('itemCount') : 0, + optionsSelected: patterns.every((pattern) => + wrapper.find(`[data-test-subj="sourcerer-combo-box"] span[title="${pattern}"]`).first().exists() + ), +}); + +const { id, patternList } = mockGlobalState.sourcerer.defaultDataView; +const patternListNoSignals = sortWithExcludesAtEnd( + patternList.filter((p) => p !== mockGlobalState.sourcerer.signalIndexName) +); +let store: ReturnType; +const sourcererDataView = { + indicesExist: true, + loading: false, +}; + +describe('Sourcerer integration tests', () => { + const state = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + kibanaDataViews: [ + mockGlobalState.sourcerer.defaultDataView, + { + ...mockGlobalState.sourcerer.defaultDataView, + id: '1234', + title: 'fakebeat-*,neatbeat-*', + patternList: ['fakebeat-*'], + }, + ], + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.default]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default], + loading: false, + selectedDataViewId: id, + selectedPatterns: patternListNoSignals.slice(0, 2), + }, + }, + }, + }; + + const { storage } = createSecuritySolutionStorageMock(); + + beforeEach(() => { + const pollForSignalIndexMock = jest.fn(); + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + + (useSourcererDataView as jest.Mock).mockReturnValue({ + ...sourcererDataView, + activePatterns: ['myFakebeat-*'], + }); + store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + jest.clearAllMocks(); + }); + + it('Selects a different index pattern', async () => { + const wrapper = mount( + + + + ); + wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); + wrapper.find(`button[data-test-subj="sourcerer-select"]`).first().simulate('click'); + + wrapper.find(`[data-test-subj="dataView-option-super"]`).first().simulate('click'); + expect(checkOptionsAndSelections(wrapper, ['fakebeat-*'])).toEqual({ + availableOptionCount: 0, + optionsSelected: true, + }); + wrapper.find(`button[data-test-subj="sourcerer-save"]`).first().simulate('click'); + + expect(mockDispatch).toHaveBeenCalledWith( + sourcererActions.setSelectedDataView({ + id: SourcererScopeName.default, + selectedDataViewId: '1234', + selectedPatterns: ['fakebeat-*'], + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx index 7653a0830b70e..1c2c73abcc042 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx @@ -107,6 +107,7 @@ export const TemporarySourcererComp = React.memo( const timelineType = useDeepEqualSelector( (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults).timelineType ); + return ( <> ( )} {isModified === 'missingPatterns' && ( <> - {missingPatterns.join(', ')}, - }} - /> + + {missingPatterns.join(', ')}, + }} + /> + )} diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/timeline_sourcerer.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/timeline_sourcerer.test.tsx new file mode 100644 index 0000000000000..6f57f5fc2d34f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/timeline_sourcerer.test.tsx @@ -0,0 +1,188 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { render, cleanup, fireEvent, screen, waitFor } from '@testing-library/react'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { Sourcerer } from '.'; +import { sourcererModel } from '../../store/sourcerer'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../mock'; +import { createStore } from '../../store'; +import { useSourcererDataView } from '../../containers/sourcerer'; +import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'; + +const mockDispatch = jest.fn(); + +jest.mock('../../containers/sourcerer'); +jest.mock('../../containers/sourcerer/use_signal_helpers'); +const mockUseUpdateDataView = jest.fn().mockReturnValue(() => true); +jest.mock('./use_update_data_view', () => ({ + useUpdateDataView: () => mockUseUpdateDataView, +})); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + + return { + ...original, + toMountPoint: jest.fn(), + }; +}); + +const mockUpdateUrlParam = jest.fn(); +jest.mock('../../utils/global_query_string', () => { + const original = jest.requireActual('../../utils/global_query_string'); + + return { + ...original, + useUpdateUrlParam: () => mockUpdateUrlParam, + }; +}); + +const { id } = mockGlobalState.sourcerer.defaultDataView; + +let store: ReturnType; +const sourcererDataView = { + indicesExist: true, + loading: false, +}; + +describe('timeline sourcerer', () => { + const { storage } = createSecuritySolutionStorageMock(); + store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + const testProps = { + scope: sourcererModel.SourcererScopeName.timeline, + }; + + beforeEach(async () => { + const pollForSignalIndexMock = jest.fn(); + + (useSourcererDataView as jest.Mock).mockReturnValue(sourcererDataView); + + (useSignalHelpers as jest.Mock).mockReturnValue({ + pollForSignalIndex: pollForSignalIndexMock, + signalIndexNeedsInit: false, + }); + + render( + + + + ); + + fireEvent.click(screen.getByTestId('timeline-sourcerer-trigger')); + + await waitFor(() => { + fireEvent.click(screen.getByTestId(`sourcerer-advanced-options-toggle`)); + }); + }); + + afterEach(() => { + cleanup(); + }); + + it('renders "alerts only" checkbox, unchecked', async () => { + await waitFor(() => { + expect(screen.getByTestId('sourcerer-alert-only-checkbox').parentElement).toHaveTextContent( + 'Show only detection alerts' + ); + expect(screen.getByTestId('sourcerer-alert-only-checkbox')).not.toBeChecked(); + }); + + fireEvent.click(screen.getByTestId('sourcerer-alert-only-checkbox')); + + await waitFor(() => { + expect(screen.getByTestId('sourcerer-alert-only-checkbox')).toBeChecked(); + }); + }); + + it('data view selector is enabled', async () => { + await waitFor(() => { + expect(screen.getByTestId('sourcerer-select')).toBeEnabled(); + }); + }); + + it('data view selector is default to Security Default Data View', async () => { + await waitFor(() => { + expect(screen.getByTestId('security-option-super')).toHaveTextContent( + 'Security Default Data View' + ); + }); + }); + + it('index pattern selector is enabled', async () => { + await waitFor(() => { + expect(screen.getByTestId('sourcerer-combo-box')).toBeEnabled(); + }); + }); + + it('render reset button', async () => { + await waitFor(() => { + expect(screen.getByTestId('sourcerer-reset')).toBeVisible(); + }); + }); + + it('render save button', async () => { + await waitFor(() => { + expect(screen.getByTestId('sourcerer-save')).toBeVisible(); + }); + }); + + it('Checks box when only alerts index is selected in timeline', async () => { + cleanup(); + const state2 = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.timeline]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.timeline], + loading: false, + selectedDataViewId: id, + selectedPatterns: [`${mockGlobalState.sourcerer.signalIndexName}`], + }, + }, + }, + }; + + store = createStore( + state2, + SUB_PLUGINS_REDUCER, + + kibanaObservable, + storage + ); + + render( + + + + ); + + fireEvent.click(screen.queryAllByTestId('timeline-sourcerer-trigger')[0]); + + await waitFor(() => { + expect(screen.getByTestId('sourcerer-alert-only-checkbox')).toBeChecked(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/trigger.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/trigger.tsx index e1c1e405bd52b..5dc7ab8522189 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/trigger.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/trigger.tsx @@ -7,9 +7,9 @@ import type { FC } from 'react'; import React, { memo, useMemo } from 'react'; -import { EuiToolTip } from '@elastic/eui'; +import { EuiToolTip, EuiButton } from '@elastic/eui'; import * as i18n from './translations'; -import { getTooltipContent, StyledBadge, StyledButton } from './helpers'; +import { getTooltipContent, StyledBadge, StyledButtonEmpty } from './helpers'; import type { ModifiedTypes } from './use_pick_index_patterns'; interface Props { @@ -68,12 +68,17 @@ export const TriggerComponent: FC = ({ } }, [isModified]); + const Button = useMemo( + () => (isTimelineSourcerer ? EuiButton : StyledButtonEmpty), + [isTimelineSourcerer] + ); + const trigger = useMemo( () => ( - = ({ > {i18n.DATA_VIEW} {!disabled && badge} - + ), - [disabled, badge, isTimelineSourcerer, loading, onClick] + [disabled, badge, isTimelineSourcerer, loading, onClick, Button] ); const tooltipContent = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/utils.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/utils.tsx index 00c6219598729..2e6653a185952 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/utils.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/utils.tsx @@ -43,28 +43,30 @@ export const CurrentPatternsMessage = ({ if (timelineType === TimelineType.template) { return ( + + {activePatterns.join(', ')}, + }} + /> + + ); + } + + return ( + {activePatterns.join(', ')}, }} /> - ); - } - - return ( - {activePatterns.join(', ')}, - }} - /> + ); }; @@ -147,25 +149,27 @@ export const DeprecatedMessage = ({ }) => { if (timelineType === TimelineType.template) { return ( + + {i18n.TOGGLE_TO_NEW_SOURCERER}, + }} + /> + + ); + } + return ( + {i18n.TOGGLE_TO_NEW_SOURCERER}, }} /> - ); - } - return ( - {i18n.TOGGLE_TO_NEW_SOURCERER}, - }} - /> + ); }; @@ -178,24 +182,26 @@ export const MissingPatternsMessage = ({ }) => { if (timelineType === TimelineType.template) { return ( + + {i18n.TOGGLE_TO_NEW_SOURCERER}, + }} + /> + + ); + } + return ( + {i18n.TOGGLE_TO_NEW_SOURCERER}, }} /> - ); - } - return ( - {i18n.TOGGLE_TO_NEW_SOURCERER}, - }} - /> + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx index aa168343cdb90..924b1158593a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.test.tsx @@ -101,6 +101,7 @@ describe('VisualizationActions', () => { .fn() .mockReturnValue({ open: mockGetCreateCaseFlyoutOpen }), }, + helpers: { canUseCases: jest.fn().mockReturnValue(allCasesPermissions()) }, }, application: { capabilities: { [CASES_FEATURE_ID]: allCasesCapabilities() }, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx index 6118bc4441420..cc03f80daf95b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx @@ -8,7 +8,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__'; import { kpiHostMetricLensAttributes } from './lens_attributes/hosts/kpi_host_metric'; import { useAddToExistingCase } from './use_add_to_existing_case'; -import { useGetUserCasesPermissions } from '../../lib/kibana'; import { allCasesPermissions, readCasesPermissions, @@ -18,13 +17,13 @@ import { AttachmentType } from '@kbn/cases-plugin/common'; const mockedUseKibana = mockUseKibana(); const mockGetUseCasesAddToExistingCaseModal = jest.fn(); +const mockCanUseCases = jest.fn(); jest.mock('../../lib/kibana', () => { const original = jest.requireActual('../../lib/kibana'); return { ...original, - useGetUserCasesPermissions: jest.fn(), useKibana: () => ({ ...mockedUseKibana, services: { @@ -33,6 +32,7 @@ jest.mock('../../lib/kibana', () => { hooks: { useCasesAddToExistingCaseModal: mockGetUseCasesAddToExistingCaseModal, }, + helpers: { canUseCases: mockCanUseCases }, }, }, }), @@ -47,7 +47,7 @@ describe('useAddToExistingCase', () => { }; beforeEach(() => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(allCasesPermissions()); + mockCanUseCases.mockReturnValue(allCasesPermissions()); }); it('useCasesAddToExistingCaseModal with attachments', () => { @@ -68,7 +68,7 @@ describe('useAddToExistingCase', () => { }); it("disables the button if the user can't create but can read", () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(readCasesPermissions()); + mockCanUseCases.mockReturnValue(readCasesPermissions()); const { result } = renderHook(() => useAddToExistingCase({ @@ -81,7 +81,7 @@ describe('useAddToExistingCase', () => { }); it("disables the button if the user can't read but can create", () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(writeCasesPermissions()); + mockCanUseCases.mockReturnValue(writeCasesPermissions()); const { result } = renderHook(() => useAddToExistingCase({ diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx index 9a9c239c65e91..8f28e9534e6a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.tsx @@ -8,7 +8,8 @@ import { useCallback, useMemo } from 'react'; import { AttachmentType, LENS_ATTACHMENT_TYPE } from '@kbn/cases-plugin/common'; import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; -import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana'; +import { APP_ID } from '../../../../common'; +import { useKibana } from '../../lib/kibana'; import { ADD_TO_CASE_SUCCESS } from './translations'; import type { LensAttributes } from './types'; @@ -21,8 +22,8 @@ export const useAddToExistingCase = ({ lensAttributes: LensAttributes | null; timeRange: { from: string; to: string } | null; }) => { - const userCasesPermissions = useGetUserCasesPermissions(); const { cases } = useKibana().services; + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const attachments = useMemo(() => { return [ { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx index 29969d489a038..91347dc9fe073 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx @@ -8,7 +8,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__'; import { kpiHostMetricLensAttributes } from './lens_attributes/hosts/kpi_host_metric'; import { useAddToNewCase } from './use_add_to_new_case'; -import { useGetUserCasesPermissions } from '../../lib/kibana'; import { allCasesPermissions, readCasesPermissions, @@ -20,13 +19,13 @@ jest.mock('../../lib/kibana/kibana_react'); const mockedUseKibana = mockUseKibana(); const mockGetUseCasesAddToNewCaseFlyout = jest.fn(); +const mockCanUseCases = jest.fn(); jest.mock('../../lib/kibana', () => { const original = jest.requireActual('../../lib/kibana'); return { ...original, - useGetUserCasesPermissions: jest.fn(), useKibana: () => ({ ...mockedUseKibana, services: { @@ -35,6 +34,7 @@ jest.mock('../../lib/kibana', () => { hooks: { useCasesAddToNewCaseFlyout: mockGetUseCasesAddToNewCaseFlyout, }, + helpers: { canUseCases: mockCanUseCases }, }, }, }), @@ -47,7 +47,7 @@ describe('useAddToNewCase', () => { to: '2022-03-07T15:59:59.999Z', }; beforeEach(() => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(allCasesPermissions()); + mockCanUseCases.mockReturnValue(allCasesPermissions()); }); it('useCasesAddToNewCaseFlyout with attachments', () => { @@ -64,7 +64,7 @@ describe('useAddToNewCase', () => { }); it("disables the button if the user can't create but can read", () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(readCasesPermissions()); + mockCanUseCases.mockReturnValue(readCasesPermissions()); const { result } = renderHook(() => useAddToNewCase({ @@ -76,7 +76,7 @@ describe('useAddToNewCase', () => { }); it("disables the button if the user can't read but can create", () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(writeCasesPermissions()); + mockCanUseCases.mockReturnValue(writeCasesPermissions()); const { result } = renderHook(() => useAddToNewCase({ diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx index 6a395af34b445..68f730b376dcf 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.tsx @@ -8,7 +8,8 @@ import { useCallback, useMemo } from 'react'; import { AttachmentType, LENS_ATTACHMENT_TYPE } from '@kbn/cases-plugin/common'; import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; -import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana'; +import { APP_ID } from '../../../../common'; +import { useKibana } from '../../lib/kibana'; import { ADD_TO_CASE_SUCCESS } from './translations'; import type { LensAttributes } from './types'; @@ -20,8 +21,9 @@ export interface UseAddToNewCaseProps { } export const useAddToNewCase = ({ onClick, timeRange, lensAttributes }: UseAddToNewCaseProps) => { - const userCasesPermissions = useGetUserCasesPermissions(); const { cases } = useKibana().services; + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); + const attachments = useMemo(() => { return [ { diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts index c16f5ebb4bae5..4d6ef73c643de 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { useCallback } from 'react'; +import { useCallback, useEffect } from 'react'; +import { safeDecode } from '@kbn/rison'; import { useDispatch } from 'react-redux'; @@ -40,5 +41,23 @@ export const useInitTimelineFromUrlParam = () => { [dispatch] ); + useEffect(() => { + const listener = () => { + const timelineState = new URLSearchParams(window.location.search).get(URL_PARAM_KEY.timeline); + + if (!timelineState) { + return; + } + + const parsedState = safeDecode(timelineState) as TimelineUrl | null; + + onInitialize(parsedState); + }; + + // This is needed to initialize the timeline from the URL when the user clicks the back / forward buttons + window.addEventListener('popstate', listener); + return () => window.removeEventListener('popstate', listener); + }, [onInitialize]); + useInitializeUrlParam(URL_PARAM_KEY.timeline, onInitialize); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_contract_component.ts b/x-pack/plugins/security_solution/public/common/hooks/use_contract_component.ts new file mode 100644 index 0000000000000..2ebb46db35c83 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_contract_component.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { useKibana } from '../lib/kibana'; + +export const useContractComponents = () => { + const { getComponents$ } = useKibana().services; + const components$ = useMemo(() => getComponents$(), [getComponents$]); + return useObservable(components$, {}); +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts index 30d914f5ccc83..cf5616e0c33fe 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { EXPANDABLE_FLYOUT_URL_KEY } from '@kbn/expandable-flyout'; import { useSyncGlobalQueryString } from '../utils/global_query_string'; import { useInitSearchBarFromUrlParams } from './search_bar/use_init_search_bar_url_params'; import { useInitTimerangeFromUrlParam } from './search_bar/use_init_timerange_url_params'; @@ -15,9 +16,6 @@ import { useQueryTimelineByIdOnUrlChange } from './timeline/use_query_timeline_b import { useInitFlyoutFromUrlParam } from './flyout/use_init_flyout_url_param'; import { useSyncFlyoutUrlParam } from './flyout/use_sync_flyout_url_param'; -// NOTE: the expandable flyout package url state is handled here: -// x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx - export const useUrlState = () => { useSyncGlobalQueryString(); useInitSearchBarFromUrlParams(); @@ -30,14 +28,14 @@ export const useUrlState = () => { useSyncFlyoutUrlParam(); }; -export enum URL_PARAM_KEY { - appQuery = 'query', - eventFlyout = 'eventFlyout', - filters = 'filters', - savedQuery = 'savedQuery', - sourcerer = 'sourcerer', - timeline = 'timeline', - timerange = 'timerange', - pageFilter = 'pageFilters', - rulesTable = 'rulesTable', -} +export const URL_PARAM_KEY = { + appQuery: 'query', + eventFlyout: EXPANDABLE_FLYOUT_URL_KEY, + filters: 'filters', + savedQuery: 'savedQuery', + sourcerer: 'sourcerer', + timeline: 'timeline', + timerange: 'timerange', + pageFilter: 'pageFilters', + rulesTable: 'rulesTable', +} as const; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts index 954f4fd0b74bc..a0f59fb18f3f8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts @@ -95,7 +95,6 @@ export const useToasts = jest export const useCurrentUser = jest.fn(); export const withKibana = jest.fn(createWithKibanaMock()); export const KibanaContextProvider = jest.fn(createKibanaContextProviderMock()); -export const useGetUserCasesPermissions = jest.fn(); export const useAppUrl = jest.fn().mockReturnValue({ getAppUrl: jest .fn() diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts index 043d1a0ab36f0..714049872ee5d 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts @@ -12,9 +12,8 @@ import { i18n } from '@kbn/i18n'; import { camelCase, isArray, isObject } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import type { Capabilities } from '@kbn/core/public'; -import type { CasesPermissions } from '@kbn/cases-plugin/common'; import { useGetAppUrl, useNavigateTo, @@ -22,11 +21,7 @@ import { type GetAppUrl, type NavigateTo, } from '@kbn/security-solution-navigation'; -import { - CASES_FEATURE_ID, - DEFAULT_DATE_FORMAT, - DEFAULT_DATE_FORMAT_TZ, -} from '../../../../common/constants'; +import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ } from '../../../../common/constants'; import { errorToToaster, useStateToaster } from '../../components/toasters'; import type { StartServices } from '../../../types'; import { useUiSetting, useKibana } from './kibana_react'; @@ -153,44 +148,6 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => { return user; }; -export const useGetUserCasesPermissions = () => { - const [casesPermissions, setCasesPermissions] = useState({ - all: false, - create: false, - read: false, - update: false, - delete: false, - push: false, - connectors: false, - }); - const uiCapabilities = useKibana().services.application.capabilities; - const casesCapabilities = useKibana().services.cases.helpers.getUICapabilities( - uiCapabilities[CASES_FEATURE_ID] - ); - - useEffect(() => { - setCasesPermissions({ - all: casesCapabilities.all, - create: casesCapabilities.create, - read: casesCapabilities.read, - update: casesCapabilities.update, - delete: casesCapabilities.delete, - push: casesCapabilities.push, - connectors: casesCapabilities.connectors, - }); - }, [ - casesCapabilities.all, - casesCapabilities.create, - casesCapabilities.read, - casesCapabilities.update, - casesCapabilities.delete, - casesCapabilities.push, - casesCapabilities.connectors, - ]); - - return casesPermissions; -}; - export const useAppUrl = useGetAppUrl; export { useNavigateTo, useNavigation }; export type { GetAppUrl, NavigateTo }; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 3d6d1a7034d28..ffbd26b97028a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -117,7 +117,7 @@ export const createStartServicesMock = ( const discover = discoverPluginMock.createStartContract(); const cases = mockCasesContract(); const dataViewServiceMock = dataViewPluginMocks.createStartContract(); - cases.helpers.getUICapabilities.mockReturnValue(noCasesPermissions()); + cases.helpers.canUseCases.mockReturnValue(noCasesPermissions()); const triggersActionsUi = triggersActionsUiMock.createStart(); const cloudExperiments = cloudExperimentsMock.createStartMock(); const guidedOnboarding = guidedOnboardingMock.createStart(); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/services.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/services.ts index fff266b8a5cda..a3d67524935cc 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/services.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/services.ts @@ -9,7 +9,7 @@ import type { CoreStart } from '@kbn/core/public'; import type { StartPlugins } from '../../../types'; type GlobalServices = Pick & - Pick; + Pick; export class KibanaServices { private static buildFlavor?: string; @@ -30,6 +30,7 @@ export class KibanaServices { uiSettings, notifications, expressions, + savedSearch, }: GlobalServices & { kibanaBranch: string; kibanaVersion: string; @@ -44,6 +45,7 @@ export class KibanaServices { unifiedSearch, notifications, expressions, + savedSearch, }; this.kibanaBranch = kibanaBranch; this.kibanaVersion = kibanaVersion; diff --git a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts index 3fefc26076a12..80cf74a8f55b7 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts @@ -233,7 +233,7 @@ export const convertToBuildEsQuery = ({ export const combineQueries = ({ config, - dataProviders, + dataProviders = [], indexPattern, browserFields, filters = [], diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index a118dfc86545c..f770a26405fef 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -372,6 +372,8 @@ export const mockGlobalState: State = { itemsPerPageOptions: [10, 25, 50, 100], savedSearchId: null, isDiscoverSavedSearchLoaded: false, + savedSearch: null, + isDataProviderVisible: true, }, }, insertTimeline: null, 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 a6779272da763..03dc789a41ad1 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 @@ -21,7 +21,6 @@ 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 { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks'; import { useKibana } from '../lib/kibana'; import { UpsellingProvider } from '../components/upselling_provider'; import { MockAssistantProvider } from './mock_assistant_provider'; @@ -76,29 +75,27 @@ export const TestProvidersComponent: React.FC = ({ return ( - - - - ({ eui: euiDarkVars, darkMode: true })}> - - - - - - Promise.resolve(cellActions)} - > - {children} - - - - - - - - - - + + + ({ eui: euiDarkVars, darkMode: true })}> + + + + + + Promise.resolve(cellActions)} + > + {children} + + + + + + + + + ); @@ -130,33 +127,31 @@ const TestProvidersWithPrivilegesComponent: React.FC = ({ return ( - - - ({ eui: euiDarkVars, darkMode: true })}> - - - - + ({ eui: euiDarkVars, darkMode: true })}> + + + + + Promise.resolve(cellActions)} > - Promise.resolve(cellActions)} - > - {children} - - - - - - - - + {children} + + + + + + + ); diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index ef6b4d265a5a7..ba567b623ab51 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -2027,6 +2027,8 @@ export const mockTimelineModel: TimelineModel = { templateTimelineVersion: null, version: '1', savedSearchId: null, + savedSearch: null, + isDataProviderVisible: false, }; export const mockDataTableModel: DataTableModel = { @@ -2208,6 +2210,8 @@ export const defaultTimelineProps: CreateTimelineProps = { version: null, savedSearchId: null, isDiscoverSavedSearchLoaded: false, + savedSearch: null, + isDataProviderVisible: false, }, to: '2018-11-05T19:03:25.937Z', notes: null, diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts index a5b3bb1c8d31f..1be01959e2d0a 100644 --- a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts @@ -97,3 +97,10 @@ export const useReplaceUrlParams = (): ((params: Record { + // NOTE: This is a workaround to make sure that new history entry is created as a result of the user action. + // This is needed because of the way global url state is handled in the security app. + // (it defaults to replace the url params instead of pushing new history entry) + window.history.pushState({}, '', window.location.href); +}; diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx index 30f9483169b13..07c87aec59da6 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.test.tsx @@ -83,7 +83,7 @@ describe('use show timeline', () => { }); it('hides timeline for blacklist routes', async () => { - mockUseLocation.mockReturnValueOnce({ pathname: '/rules/create' }); + mockUseLocation.mockReturnValueOnce({ pathname: '/rules/add_rules' }); await act(async () => { const { result, waitForNextUpdate } = renderHook(() => useShowTimeline()); await waitForNextUpdate(); diff --git a/x-pack/plugins/security_solution/public/contract_components.ts b/x-pack/plugins/security_solution/public/contract_components.ts index 91e341c4120f1..3d1dee67eab94 100644 --- a/x-pack/plugins/security_solution/public/contract_components.ts +++ b/x-pack/plugins/security_solution/public/contract_components.ts @@ -5,17 +5,16 @@ * 2.0. */ -import { BehaviorSubject, map } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import type { Observable } from 'rxjs'; -export type ContractComponentName = 'getStarted' | 'dashboardsLandingCallout'; - -export type ContractComponents = Partial>; +export type ContractComponents = Partial<{ + GetStarted: React.ComponentType<{}>; + DashboardsLandingCallout: React.ComponentType<{}>; +}>; export type SetComponents = (components: ContractComponents) => void; -export type GetComponent$ = ( - name: ContractComponentName -) => Observable; +export type GetComponents$ = () => Observable; export class ContractComponentsService { private components$: BehaviorSubject; @@ -28,6 +27,5 @@ export class ContractComponentsService { this.components$.next(components); }; - public getComponent$: GetComponent$ = (name) => - this.components$.pipe(map((components) => components[name])); + public getComponents$ = () => this.components$.asObservable(); } diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx index 8723bfb69f326..52e6692de104b 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx @@ -28,11 +28,9 @@ jest.mock('@kbn/dashboard-plugin/public', () => ({ DashboardTopNav: jest.fn().mockReturnValue(), })); -const mockUseObservable = jest.fn(); - -jest.mock('react-use', () => ({ - ...jest.requireActual('react-use'), - useObservable: () => mockUseObservable(), +const mockUseContractComponents = jest.fn(() => ({})); +jest.mock('../../../common/hooks/use_contract_component', () => ({ + useContractComponents: () => mockUseContractComponents(), })); const DEFAULT_DASHBOARD_CAPABILITIES = { show: true, createNew: true }; @@ -216,11 +214,11 @@ describe('Dashboards landing', () => { }); it('should render callout when available', async () => { - const DummyComponent = () => ; - mockUseObservable.mockReturnValue(); + const DashboardsLandingCallout = () => ; + mockUseContractComponents.mockReturnValue({ DashboardsLandingCallout }); await renderDashboardLanding(); - expect(screen.queryByTestId('test')).toBeInTheDocument(); + expect(screen.queryByTestId('callout-test')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 10fb3c060548f..affe83b0682ca 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -17,11 +17,11 @@ import React, { useCallback, useMemo } from 'react'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import { LandingLinksImageCards } from '@kbn/security-solution-navigation/landing_links'; -import { useObservable } from 'react-use'; +import { useContractComponents } from '../../../common/hooks/use_contract_component'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { SecurityPageName } from '../../../../common/constants'; -import { useCapabilities, useKibana, useNavigateTo } from '../../../common/lib/kibana'; +import { useCapabilities, useNavigateTo } from '../../../common/lib/kibana'; import { useRootNavLink } from '../../../common/links/nav_links'; import { Title } from '../../../common/components/header_page/title'; import { LinkButton } from '../../../common/components/links/helpers'; @@ -83,8 +83,7 @@ const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }; export const DashboardsLandingPage = () => { - const { getComponent$ } = useKibana().services; - const dashboardLandingCallout = useObservable(getComponent$('dashboardsLandingCallout')); + const { DashboardsLandingCallout } = useContractComponents(); const { links = [] } = useRootNavLink(SecurityPageName.dashboards) ?? {}; const urlState = useGlobalQueryString(); const { show: canReadDashboard, createNew: canCreateDashboard } = @@ -122,9 +121,9 @@ export const DashboardsLandingPage = () => {
    - {dashboardLandingCallout && ( + {DashboardsLandingCallout && ( <> - {dashboardLandingCallout} + )} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index dc762337f5e0e..282d3fcc8439a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -25,6 +25,7 @@ import type { Threats, } from '@kbn/securitysolution-io-ts-alerting-types'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { SeverityBadge } from '../../../../detections/components/rules/severity_badge'; import { defaultToEmptyTag } from '../../../../common/components/empty_value'; @@ -333,7 +334,9 @@ const prepareAboutSectionListItems = ( ) : ( '' ), - description: , + description: ( + + ), }; }) ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/osquery/osquery_response_action_form_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/osquery/osquery_response_action_form_field.tsx index ada8c4a81e50b..7898dfef3ea90 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/osquery/osquery_response_action_form_field.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_response_actions/osquery/osquery_response_action_form_field.tsx @@ -8,11 +8,13 @@ import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import React, { useCallback, useMemo } from 'react'; import { isEmpty, map } from 'lodash'; +import { useFormContext } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { useKibana } from '../../../common/lib/kibana'; export const ResponseActionFormField = React.memo(({ field }: { field: FieldHook }) => { - const { setErrors, clearErrors, value, setValue } = field; + const { clearErrors, value, setValue, path } = field; const { osquery } = useKibana().services; + const context = useFormContext(); const OsqueryForm = useMemo( () => osquery?.OsqueryResponseActionTypeForm, @@ -24,10 +26,11 @@ export const ResponseActionFormField = React.memo(({ field }: { field: FieldHook if (isEmpty(newErrors)) { clearErrors(); } else { - setErrors(map(newErrors, (error) => ({ message: error.message }))); + const errors = map(newErrors, (error) => ({ message: error.message })); + context.setFieldErrors(path, errors); } }, - [setErrors, clearErrors] + [clearErrors, context, path] ); // @ts-expect-error update types diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 23b8fe50be532..6f7521c3c1d60 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -452,10 +452,11 @@ describe('alert actions', () => { templateTimelineVersion: null, version: null, savedSearchId: null, + savedSearch: null, isDiscoverSavedSearchLoaded: false, + isDataProviderVisible: false, }, to: '2018-11-05T19:03:25.937Z', - resolveTimelineConfig: undefined, ruleNote: '# this is some markdown documentation', ruleAuthor: ['elastic'], }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx index 2b887808696bd..a8e13bb8c5c27 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.test.tsx @@ -74,17 +74,22 @@ jest.mock('../../../../common/lib/kibana', () => { application: { capabilities: { siem: { crud_alerts: true, read_alerts: true } }, }, - cases: mockCasesContract(), + cases: { + ...mockCasesContract(), + helpers: { + canUseCases: jest.fn().mockReturnValue({ + all: true, + create: true, + read: true, + update: true, + delete: true, + push: true, + }), + getRuleIdFromEvent: jest.fn(), + }, + }, }, }), - useGetUserCasesPermissions: jest.fn().mockReturnValue({ - all: true, - create: true, - read: true, - update: true, - delete: true, - push: true, - }), }; }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.test.tsx index de33379f48aba..cbe56a62c4574 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.test.tsx @@ -12,7 +12,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useAddToCaseActions } from './use_add_to_case_actions'; import { TestProviders } from '../../../../common/mock'; -import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '../../../../common/lib/kibana'; import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; import { AlertsCasesTourSteps, @@ -20,6 +20,7 @@ import { } from '../../../../common/components/guided_onboarding_tour/tour_config'; import { CasesTourSteps } from '../../../../common/components/guided_onboarding_tour/cases_tour_steps'; import type { AlertTableContextMenuItem } from '../types'; +import { allCasesPermissions } from '../../../../cases_test_utils'; jest.mock('../../../../common/components/guided_onboarding_tour'); jest.mock('../../../../common/lib/kibana'); @@ -76,15 +77,6 @@ describe('useAddToCaseActions', () => { isTourShown: () => false, }); - (useGetUserCasesPermissions as jest.Mock).mockReturnValue({ - all: true, - create: true, - read: true, - update: true, - delete: true, - push: true, - }); - useKibanaMock.mockReturnValue({ services: { cases: { @@ -94,6 +86,7 @@ describe('useAddToCaseActions', () => { }, helpers: { getRuleIdFromEvent: () => null, + canUseCases: jest.fn().mockReturnValue(allCasesPermissions()), }, }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx index 821a638e893c2..de3c8782722fa 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useMemo } from 'react'; import { AttachmentType } from '@kbn/cases-plugin/common'; import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { APP_ID } from '../../../../../common'; import { CasesTourSteps } from '../../../../common/components/guided_onboarding_tour/cases_tour_steps'; import { AlertsCasesTourSteps, @@ -16,7 +17,7 @@ import { SecurityStepId, } from '../../../../common/components/guided_onboarding_tour/tour_config'; import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; -import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '../../../../common/lib/kibana'; import type { TimelineNonEcsData } from '../../../../../common/search_strategy'; import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from '../translations'; import type { AlertTableContextMenuItem } from '../types'; @@ -43,7 +44,7 @@ export const useAddToCaseActions = ({ refetch, }: UseAddToCaseActions) => { const { cases: casesUi } = useKibana().services; - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = casesUi.helpers.canUseCases([APP_ID]); const isAlert = useMemo(() => { return ecsData?.event?.kind?.includes('signal'); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index 6fe5693796cbf..02d149860c1b4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -18,6 +18,7 @@ import { useApi } from '@kbn/securitysolution-list-hooks'; import type { Filter } from '@kbn/es-query'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { createHistoryEntry } from '../../../../common/utils/global_query_string/helpers'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; @@ -175,6 +176,8 @@ export const useInvestigateInTimeline = ({ ); const investigateInTimelineAlertClick = useCallback(async () => { + createHistoryEntry(); + startTransaction({ name: ALERTS_ACTIONS.INVESTIGATE_IN_TIMELINE }); if (onInvestigateInTimelineAlertClick) { onInvestigateInTimelineAlertClick(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx index a60c24378902f..611480ddd8f29 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx @@ -30,10 +30,32 @@ import { useKibana } from '../../../../common/lib/kibana'; const TextArea = styled(EuiTextArea)` display: block; - border: ${({ theme }) => theme.eui.euiBorderThin}; - border-bottom: 0; + border: 0; box-shadow: none; + border-radius: 0px; min-height: ${({ theme }) => theme.eui.euiFormControlHeight}; + &:focus { + box-shadow: none; + } +`; + +const StyledFormRow = styled(EuiFormRow)` + border: ${({ theme }) => theme.eui.euiBorderThin}; + border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + + .euiFormRow__labelWrapper { + background: ${({ theme }) => theme.eui.euiColorLightestShade}; + border-top-left-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + border-top-right-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + padding: 8px 10px; + margin-bottom: 0px; + label { + color: ${({ theme }) => theme.eui.euiTextSubduedColor}; + &.euiFormLabel-isInvalid { + color: ${({ theme }) => theme.eui.euiColorDangerText}; + } + } + } `; export interface FieldValueQueryBar { @@ -157,7 +179,7 @@ export const EqlQueryBar: FC = ({ ); return ( - = ({ )} - + ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx index c9e74b6a9acf5..7f7a6ab21a385 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx @@ -14,7 +14,6 @@ import { EuiFlexItem, EuiFormRow, EuiLoadingSpinner, - EuiPanel, EuiPopover, EuiPopoverTitle, } from '@elastic/eui'; @@ -44,9 +43,11 @@ export interface Props { type SizeVoidFunc = (newSize: string) => void; -const Container = styled(EuiPanel)` +const Container = styled(EuiFlexGroup)` border-radius: 0; - background: ${({ theme }) => theme.eui.euiPageBackgroundColor}; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + background: ${({ theme }) => theme.eui.euiColorLightestShade}; padding: ${({ theme }) => theme.eui.euiSizeXS} ${({ theme }) => theme.eui.euiSizeS}; `; @@ -161,96 +162,113 @@ export const EqlQueryBarFooter: FC = ({ return ( - - - {errors.length > 0 && ( - - )} - {isLoading && } + + + + + {errors.length > 0 && ( + + )} + {isLoading && } + + - {!onOptionsChange && ( - - - - )} - {onOptionsChange && ( - <> - - - - - - } - isOpen={openEqlSettings} - closePopover={closeEqlSettingsHandler} - anchorPosition="downCenter" - ownFocus={false} - > - {i18n.EQL_SETTINGS_TITLE} -
    - {!isSizeOptionDisabled && ( - - + + {!onOptionsChange && ( + + + + )} + + {onOptionsChange && ( + <> + + + + + - - )} - - - - - - - - - -
    -
    -
    - - )} + {i18n.EQL_SETTINGS_TITLE} +
    + {!isSizeOptionDisabled && ( + + + + )} + + + + + + + + + +
    + + + + )} + +
    ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx index 3bee45e94712c..9a490bec1ce25 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx @@ -23,7 +23,6 @@ import { usePreviewHistogram } from './use_preview_histogram'; import { PreviewHistogram } from './preview_histogram'; import { ALL_VALUES_ZEROS_TITLE } from '../../../../common/components/charts/translation'; -import { useGetUserCasesPermissions } from '../../../../common/lib/kibana'; import { useTimelineEvents } from '../../../../common/components/events_viewer/use_timelines_events'; import { TableId } from '@kbn/securitysolution-data-table'; import { createStore } from '../../../../common/store'; @@ -58,12 +57,7 @@ const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as j const getMockUseIsExperimentalFeatureEnabled = (mockMapping?: Partial) => (flag: keyof typeof allowedExperimentalValues) => mockMapping ? mockMapping?.[flag] : allowedExperimentalValues?.[flag]; -const originalKibanaLib = jest.requireActual('../../../../common/lib/kibana'); -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); const mockUseFieldBrowserOptions = jest.fn(); jest.mock('../../../../timelines/components/fields_browser', () => ({ useFieldBrowserOptions: (props: UseFieldBrowserOptionsProps) => mockUseFieldBrowserOptions(props), diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index 356938a6d54e3..cf67bf45fd360 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -18,7 +18,7 @@ import { TimelineId } from '../../../../common/types/timeline'; import { TestProviders } from '../../../common/mock'; import { mockTimelines } from '../../../common/mock/mock_timelines_plugin'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -import { useKibana, useGetUserCasesPermissions, useHttp } from '../../../common/lib/kibana'; +import { useKibana, useHttp } from '../../../common/lib/kibana'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; import { initialUserPrivilegesState as mockInitialUserPrivilegesState } from '../../../common/components/user_privileges/user_privileges_context'; import { useUserPrivileges } from '../../../common/components/user_privileges'; @@ -46,7 +46,6 @@ jest.mock('../user_info', () => ({ })); jest.mock('../../../common/lib/kibana'); -(useGetUserCasesPermissions as jest.Mock).mockReturnValue(allCasesPermissions()); jest.mock('../../containers/detection_engine/alerts/use_alerts_privileges', () => ({ useAlertsPrivileges: jest.fn().mockReturnValue({ hasIndexWrite: true, hasKibanaCRUD: true }), @@ -119,7 +118,13 @@ describe('take action dropdown', () => { services: { ...mockStartServicesMock, timelines: { ...mockTimelines }, - cases: mockCasesContract(), + cases: { + ...mockCasesContract(), + helpers: { + canUseCases: jest.fn().mockReturnValue(allCasesPermissions()), + getRuleIdFromEvent: () => null, + }, + }, osquery: { isOsqueryAvailable: jest.fn().mockReturnValue(true), }, diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts index c2ae389a0c571..2771a8e48a04f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/translations.ts @@ -17,6 +17,6 @@ export const LISTS_PRIVILEGES_FETCH_FAILURE = i18n.translate( export const DETECTION_ENGINE_PRIVILEGES_FETCH_FAILURE = i18n.translate( 'xpack.securitySolution.containers.detectionEngine.alerts.detectionEnginePrivileges.errorFetching', { - defaultMessage: 'Failed to retreive detection engine privileges', + defaultMessage: 'Failed to retrieve detection engine privileges', } ); diff --git a/x-pack/plugins/security_solution/public/detections/pages/alerts/alert_details_redirect.tsx b/x-pack/plugins/security_solution/public/detections/pages/alerts/alert_details_redirect.tsx index 1f1ce5618abfd..f0ff67c39f0e1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alerts/alert_details_redirect.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alerts/alert_details_redirect.tsx @@ -23,7 +23,6 @@ import { URL_PARAM_KEY } from '../../../common/hooks/use_url_state'; import { inputsSelectors } from '../../../common/store'; import { formatPageFilterSearchParam } from '../../../../common/utils/format_page_filter_search_param'; import { resolveFlyoutParams } from './utils'; -import { FLYOUT_URL_PARAM } from '../../../flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url'; export const AlertDetailsRedirect = () => { const { alertId } = useParams<{ alertId: string }>(); @@ -72,7 +71,7 @@ export const AlertDetailsRedirect = () => { const pageFiltersQuery = encode(formatPageFilterSearchParam([statusPageFilter])); - const currentFlyoutParams = searchParams.get(FLYOUT_URL_PARAM); + const currentFlyoutParams = searchParams.get(URL_PARAM_KEY.eventFlyout); const [isSecurityFlyoutEnabled] = useUiSetting$(ENABLE_EXPANDABLE_FLYOUT_SETTING); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 3acccc3352a41..072649d52a6a6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -21,6 +21,7 @@ import type { import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; import type { Filter } from '@kbn/es-query'; import type { ActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { ResponseAction } from '../../../../../common/api/detection_engine/model/rule_response_actions'; import { normalizeThresholdField } from '../../../../../common/detection_engine/utils'; import { assertUnreachable } from '../../../../../common/utility_types'; @@ -253,7 +254,7 @@ export const getAboutStepsData = (rule: RuleResponse, detailsView: boolean): Abo tags, riskScore: { value: riskScore, - mapping: riskScoreMapping, + mapping: requiredOptional(riskScoreMapping), isMappingChecked: riskScoreMapping.length > 0, }, falsePositives, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index 6b92583b1ddde..b4e3734991467 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -11,6 +11,7 @@ import { RISK_ENGINE_ENABLE_URL, RISK_ENGINE_DISABLE_URL, RISK_ENGINE_INIT_URL, + RISK_ENGINE_PRIVILEGES_URL, } from '../../../common/constants'; import { KibanaServices } from '../../common/lib/kibana'; @@ -20,7 +21,8 @@ import type { GetRiskEngineStatusResponse, InitRiskEngineResponse, DisableRiskEngineResponse, -} from '../../../server/lib/entity_analytics/risk_engine/types'; + RiskEnginePrivilegesResponse, +} from '../../../server/lib/entity_analytics/types'; import type { RiskScorePreviewRequestSchema } from '../../../common/risk_engine/risk_score_preview/request_schema'; /** @@ -85,3 +87,13 @@ export const disableRiskEngine = async (): Promise => method: 'POST', }); }; + +/** + * Get risk engine privileges + */ +export const fetchRiskEnginePrivileges = async (): Promise => { + return KibanaServices.get().http.fetch(RISK_ENGINE_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts index 68b63300061b6..8b3ca2d0ac2ad 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts @@ -11,7 +11,7 @@ import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { EnableRiskEngineResponse, EnableDisableRiskEngineErrorResponse, -} from '../../../../server/lib/entity_analytics/risk_engine/types'; +} from '../../../../server/lib/entity_analytics/types'; export const DISABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'DISABLE_RISK_ENGINE']; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts index 40b3f1b4bddb6..c4d5070014fc2 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts @@ -11,7 +11,7 @@ import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { EnableRiskEngineResponse, EnableDisableRiskEngineErrorResponse, -} from '../../../../server/lib/entity_analytics/risk_engine/types'; +} from '../../../../server/lib/entity_analytics/types'; export const ENABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'ENABLE_RISK_ENGINE']; export const useEnableRiskEngineMutation = (options?: UseMutationOptions<{}>) => { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts index 35b1071b62b80..1e2a695652edf 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts @@ -11,7 +11,7 @@ import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { InitRiskEngineResponse, InitRiskEngineError, -} from '../../../../server/lib/entity_analytics/risk_engine/types'; +} from '../../../../server/lib/entity_analytics/types'; export const INIT_RISK_ENGINE_STATUS_KEY = ['POST', 'INIT_RISK_ENGINE']; diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.helpers.js b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts similarity index 52% rename from x-pack/test/api_integration/apis/management/index_management/mapping.helpers.js rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts index abb790cf2f551..2a3ffa40856cb 100644 --- a/x-pack/test/api_integration/apis/management/index_management/mapping.helpers.js +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts @@ -4,13 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { useQuery } from '@tanstack/react-query'; +import { fetchRiskEnginePrivileges } from '../api'; -import { API_BASE_PATH } from './constants'; - -export const registerHelpers = ({ supertest }) => { - const getIndexMapping = (indexName) => supertest.get(`${API_BASE_PATH}/mapping/${indexName}`); - - return { - getIndexMapping, - }; +export const useRiskEnginePrivileges = () => { + return useQuery(['GET', 'FETCH_RISK_ENGINE_PRIVILEGES'], fetchRiskEnginePrivileges); }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/index.tsx new file mode 100644 index 0000000000000..f26814431b364 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { RiskEnginePrivilegesCallOut } from './risk_engine_privileges_callout'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx new file mode 100644 index 0000000000000..edb6bc11f7217 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { CallOutMessage } from '../../../common/components/callouts'; +import { CallOutSwitcher } from '../../../common/components/callouts'; +import { MissingPrivilegesCallOutBody, MISSING_PRIVILEGES_CALLOUT_TITLE } from './translations'; +import { useMissingPrivileges } from './use_missing_risk_engine_privileges'; + +export const RiskEnginePrivilegesCallOut = () => { + const privileges = useMissingPrivileges(); + + if (privileges.isLoading || privileges.hasAllRequiredPrivileges) { + return null; + } + + const message: CallOutMessage = { + type: 'primary', + id: `missing-risk-engine-privileges`, + title: MISSING_PRIVILEGES_CALLOUT_TITLE, + description: , + }; + + return ( + message && + ); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx new file mode 100644 index 0000000000000..ed58f50f28279 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx @@ -0,0 +1,97 @@ +/* + * Copyright 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 { EuiCode, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { useKibana } from '../../../common/lib/kibana'; +import { CommaSeparatedValues } from '../../../detections/components/callouts/missing_privileges_callout/comma_separated_values'; +import type { MissingPrivileges } from './use_missing_risk_engine_privileges'; + +export const MISSING_PRIVILEGES_CALLOUT_TITLE = i18n.translate( + 'xpack.securitySolution.riskEngine.missingPrivilegesCallOut.messageTitle', + { + defaultMessage: 'Insufficient privileges', + } +); + +export const MissingPrivilegesCallOutBody: React.FC = ({ + indexPrivileges, + clusterPrivileges, +}) => { + const { docLinks } = useKibana().services; + + return ( + + + + + ), + }} + /> +

    + ), + indexPrivileges: + indexPrivileges.length > 0 ? ( + <> + +
      + {indexPrivileges.map(([index, missingPrivileges]) => ( +
    • {missingIndexPrivileges(index, missingPrivileges)}
    • + ))} +
    + + ) : null, + clusterPrivileges: + clusterPrivileges.length > 0 ? ( + <> + +
      + {clusterPrivileges.map((privilege) => ( +
    • {privilege}
    • + ))} +
    + + ) : null, + }} + /> + ); +}; + +const missingIndexPrivileges = (index: string, privileges: string[]) => ( + , + index: {index}, + }} + /> +); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts new file mode 100644 index 0000000000000..adef9e3e4909e --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import type { RiskEnginePrivilegesResponse } from '../../../../server/lib/entity_analytics/types'; +import { useRiskEnginePrivileges } from '../../api/hooks/use_risk_engine_privileges'; +import { + RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, +} from '../../../../common/risk_engine'; + +const getMissingIndexPrivileges = ( + privileges: RiskEnginePrivilegesResponse['privileges']['elasticsearch']['index'] +): MissingIndexPrivileges => { + const missingIndexPrivileges: MissingIndexPrivileges = []; + + for (const [indexName, requiredPrivileges] of Object.entries( + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES + )) { + const missingPrivileges = requiredPrivileges.filter( + (privilege) => !privileges[indexName][privilege] + ); + + if (missingPrivileges.length) { + missingIndexPrivileges.push([indexName, missingPrivileges]); + } + } + + return missingIndexPrivileges; +}; + +export type MissingClusterPrivileges = string[]; +export type MissingIndexPrivileges = Array<[indexName: string, privileges: string[]]>; + +export interface MissingPrivileges { + clusterPrivileges: MissingClusterPrivileges; + indexPrivileges: MissingIndexPrivileges; +} + +export type MissingPrivilegesResponse = + | { isLoading: true } + | { isLoading: false; hasAllRequiredPrivileges: true } + | { isLoading: false; missingPrivileges: MissingPrivileges; hasAllRequiredPrivileges: false }; + +export const useMissingPrivileges = (): MissingPrivilegesResponse => { + const { data: privilegesResponse, isLoading } = useRiskEnginePrivileges(); + + return useMemo(() => { + if (isLoading || !privilegesResponse) { + return { + isLoading: true, + }; + } + + if (privilegesResponse.has_all_required) { + return { + isLoading: false, + hasAllRequiredPrivileges: true, + }; + } + + const { privileges } = privilegesResponse; + const missinIndexPrivileges = getMissingIndexPrivileges(privileges.elasticsearch.index); + const missingClusterPrivileges = RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES.filter( + (privilege) => !privileges.elasticsearch.cluster[privilege] + ); + + return { + isLoading: false, + hasAllRequiredPrivileges: false, + missingPrivileges: { + indexPrivileges: missinIndexPrivileges, + clusterPrivileges: missingClusterPrivileges, + }, + }; + }, [isLoading, privilegesResponse]); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx index bd0b0e262e3cc..b5ccc1b8daa63 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx @@ -29,11 +29,8 @@ import { EuiAccordion, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - DETECTION_ENTITY_DASHBOARD, - RISKY_HOSTS_DOC_LINK, - RISKY_USERS_DOC_LINK, -} from '../../../common/constants'; +import { LinkAnchor } from '@kbn/security-solution-navigation/links'; +import { SecurityPageName } from '@kbn/security-solution-navigation'; import * as i18n from '../translations'; import { useRiskEngineStatus } from '../api/hooks/use_risk_engine_status'; import { useInitRiskEngineMutation } from '../api/hooks/use_init_risk_engine_mutation'; @@ -41,20 +38,8 @@ import { useEnableRiskEngineMutation } from '../api/hooks/use_enable_risk_engine import { useDisableRiskEngineMutation } from '../api/hooks/use_disable_risk_engine_mutation'; import { RiskEngineStatus, MAX_SPACES_COUNT } from '../../../common/risk_engine'; -const docsLinks = [ - { - link: DETECTION_ENTITY_DASHBOARD, - label: i18n.EA_DOCS_DASHBOARD, - }, - { - link: RISKY_HOSTS_DOC_LINK, - label: i18n.EA_DOCS_RISK_HOSTS, - }, - { - link: RISKY_USERS_DOC_LINK, - label: i18n.EA_DOCS_RISK_USERS, - }, -]; +import { RiskInformationFlyout } from '../../explore/components/risk_score/risk_information'; +import { useOnOpenCloseHandler } from '../../helper_hooks'; const MIN_WIDTH_TO_PREVENT_LABEL_FROM_MOVING = '50px'; @@ -209,6 +194,8 @@ export const RiskScoreEnableSection = () => { const closeModal = () => setIsModalVisible(false); const showModal = () => setIsModalVisible(true); + const [isFlyoutVisible, handleOnOpen, handleOnClose] = useOnOpenCloseHandler(); + const isLoading = initRiskEngineMutation.isLoading || enableRiskEngineMutation.isLoading || @@ -309,7 +296,11 @@ export const RiskScoreEnableSection = () => { )} {!isUpdateAvailable && ( - {isLoading && } + + {isLoading && ( + + )} + {
      - {docsLinks.map(({ link, label }) => ( -
    • - - {label} - - -
    • - ))} +
    • + {i18n.EA_DASHBOARD_LINK} + +
    • +
    • + + {i18n.EA_DOCS_ENTITY_RISK_SCORE} + + {isFlyoutVisible && } + +
    diff --git a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx index 4f2bacf97f02b..7bc395d279751 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx @@ -12,10 +12,16 @@ import { RiskScorePreviewSection } from '../components/risk_score_preview_sectio import { RiskScoreEnableSection } from '../components/risk_score_enable_section'; import { ENTITY_ANALYTICS_RISK_SCORE } from '../../app/translations'; import { BETA } from '../../common/translations'; +import { RiskEnginePrivilegesCallOut } from '../components/risk_engine_privileges_callout'; +import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; export const EntityAnalyticsManagementPage = () => { + const privilegesCalloutEnabled = useIsExperimentalFeatureEnabled( + 'riskEnginePrivilegesRouteEnabled' + ); return ( <> + {privilegesCalloutEnabled && } { - const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration } = - useListsConfig(); - const hasEndpointExceptionCapability = useHasSecurityCapability(capability); - - return useMemo( - () => !listsConfigLoading && !needsListsConfiguration && hasEndpointExceptionCapability, - [hasEndpointExceptionCapability, listsConfigLoading, needsListsConfiguration] - ); +): boolean => { + return useHasSecurityCapability(capability); }; diff --git a/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap b/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap index b9fd26239a829..cebd8a483aafb 100644 --- a/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap @@ -105,7 +105,7 @@ exports[`Authentication Host Table Component rendering it renders the host authe class="euiFlexItem emotion-euiFlexItem-grow-1" >

    > => [ +const getTableColumns = (riskEntity?: RiskScoreEntity): Array> => [ { field: 'level', name: i18n.INFORMATION_LEVEL_HEADER, @@ -85,9 +85,7 @@ export const RiskInformationButtonIcon = ({ riskEntity }: { riskEntity: RiskScor } data-test-subj="open-risk-information-flyout-trigger" /> - {isFlyoutVisible && ( - - )} + {isFlyoutVisible && } ); }; @@ -100,20 +98,12 @@ export const RiskInformationButtonEmpty = ({ riskEntity }: { riskEntity: RiskSco {i18n.INFO_BUTTON_TEXT} - {isFlyoutVisible && ( - - )} + {isFlyoutVisible && } ); }; -const RiskInformationFlyout = ({ - handleOnClose, - riskEntity, -}: { - handleOnClose: () => void; - riskEntity: RiskScoreEntity; -}) => { +export const RiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => void }) => { const { euiTheme } = useEuiTheme(); const simpleFlyoutTitleId = useGeneratedHtmlId({ prefix: 'RiskInformation', @@ -157,15 +147,13 @@ const RiskInformationFlyout = ({

    ), @@ -176,15 +164,13 @@ const RiskInformationFlyout = ({ ), @@ -236,26 +222,22 @@ const RiskInformationFlyout = ({

    } /> diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts index 112c6d37ca4a2..20fa22bbc26f5 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts @@ -23,7 +23,7 @@ export const INFORMATION_ARIA_LABEL = i18n.translate( } ); -export const INFORMATION_RISK_HEADER = (riskEntity: RiskScoreEntity) => +export const INFORMATION_RISK_HEADER = (riskEntity?: RiskScoreEntity) => i18n.translate('xpack.securitySolution.riskInformation.riskHeader', { defaultMessage: '{riskEntity} risk score range', values: { diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx index cae89799d2d1e..dbe56fd74931e 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx @@ -6,20 +6,31 @@ */ import { EuiLink } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; -import { RISKY_HOSTS_DOC_LINK, RISKY_USERS_DOC_LINK } from '../../../../../common/constants'; +import { + RISKY_HOSTS_DOC_LINK, + RISKY_USERS_DOC_LINK, + RISKY_ENTITY_SCORE_DOC_LINK, +} from '../../../../../common/constants'; import { LEARN_MORE } from '../../../../overview/components/entity_analytics/risk_score/translations'; const RiskScoreDocLinkComponent = ({ riskScoreEntity, title, }: { - riskScoreEntity: RiskScoreEntity; + riskScoreEntity?: RiskScoreEntity; title?: string | React.ReactNode; }) => { - const docLink = - riskScoreEntity === RiskScoreEntity.user ? RISKY_USERS_DOC_LINK : RISKY_HOSTS_DOC_LINK; + const docLink = useMemo(() => { + if (!riskScoreEntity) { + return RISKY_ENTITY_SCORE_DOC_LINK; + } + if (riskScoreEntity === RiskScoreEntity.user) { + return RISKY_USERS_DOC_LINK; + } + return RISKY_HOSTS_DOC_LINK; + }, [riskScoreEntity]); return ( diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts b/x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts index 501de5719a63e..d072607544d4f 100644 --- a/x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts +++ b/x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts @@ -24,6 +24,14 @@ export const USERS = i18n.translate('xpack.securitySolution.riskScore.overview.u defaultMessage: 'Users', }); +export const ENTITY = i18n.translate('xpack.securitySolution.riskScore.overview.entityTitle', { + defaultMessage: 'Entity', +}); + +export const ENTITIES = i18n.translate('xpack.securitySolution.riskScore.overview.entities', { + defaultMessage: 'Entities', +}); + export const RISK_SCORE_TITLE = (riskEntity: RiskScoreEntity) => i18n.translate('xpack.securitySolution.riskScore.overview.riskScoreTitle', { defaultMessage: '{riskEntity} Risk Score', @@ -41,22 +49,26 @@ export const ENTITY_RISK_LEVEL = (riskEntity: RiskScoreEntity) => }); export const getRiskEntityTranslation = ( - riskEntity: RiskScoreEntity, + riskEntity?: RiskScoreEntity, lowercase = false, plural = false ) => { - if (lowercase) { - if (plural) { - return (riskEntity === RiskScoreEntity.host ? HOSTS : USERS).toLowerCase(); - } + const text = getRiskEntityTranslationText(riskEntity, plural); + return lowercase ? text.toLowerCase() : text; +}; - return (riskEntity === RiskScoreEntity.host ? HOST : USER).toLowerCase(); - } - if (plural) { - return riskEntity === RiskScoreEntity.host ? HOSTS : USERS; +export const getRiskEntityTranslationText = ( + riskEntity: RiskScoreEntity | undefined, + plural: boolean +) => { + switch (riskEntity) { + case RiskScoreEntity.host: + return plural ? HOSTS : HOST; + case RiskScoreEntity.user: + return plural ? USERS : USER; + default: + return plural ? ENTITIES : ENTITY; } - - return riskEntity === RiskScoreEntity.host ? HOST : USER; }; export const ALERTS = i18n.translate('xpack.securitySolution.riskScore.overview.alerts', { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx index 95b4e0a600594..7ba849e1fec1b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx @@ -18,6 +18,7 @@ jest.mock('../../shared/hooks/use_investigation_guide'); const NO_DATA_TEXT = "There's no investigation guide for this rule. Edit the rule's settingsExternal link(opens in a new tab or window) to add one."; +const PREVIEW_MESSAGE = 'Investigation guide is not available in alert preview.'; const renderInvestigationGuide = (context: LeftPanelContext = mockContextValue) => ( @@ -76,4 +77,15 @@ describe('', () => { const { getByTestId } = render(renderInvestigationGuide()); expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_TEXT); }); + + it('should render preview message when flyout is in preview', () => { + (useInvestigationGuide as jest.Mock).mockReturnValue({ + loading: false, + error: true, + }); + const { getByTestId } = render( + renderInvestigationGuide({ ...mockContextValue, isPreview: true }) + ); + expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(PREVIEW_MESSAGE); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.tsx index bffe966b944b2..d061cbb25c4c8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.tsx @@ -18,7 +18,7 @@ import { FlyoutLoading } from '../../../shared/components/flyout_loading'; * Renders a message saying the guide hasn't been set up or the full investigation guide. */ export const InvestigationGuide: React.FC = () => { - const { dataFormattedForFieldBrowser } = useLeftPanelContext(); + const { dataFormattedForFieldBrowser, isPreview } = useLeftPanelContext(); const { loading, error, basicAlertData, ruleNote } = useInvestigationGuide({ dataFormattedForFieldBrowser, @@ -26,7 +26,12 @@ export const InvestigationGuide: React.FC = () => { return (
    - {loading ? ( + {isPreview ? ( + + ) : loading ? ( ) : !error && basicAlertData.ruleId && ruleNote ? ( { const NO_DATA_MESSAGE = "There are no response actions defined for this event. To add some, edit the rule's settings and set up response actionsExternal link(opens in a new tab or window)."; +const PREVIEW_MESSAGE = 'Response is not available in alert preview.'; const defaultContextValue = { dataAsNestedObject: { @@ -139,4 +140,10 @@ describe('', () => { expect(wrapper.getByTestId(RESPONSE_DETAILS_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE); }); + + it('should render preview message if flyout is in preview', () => { + const wrapper = renderResponseDetails({ ...defaultContextValue, isPreview: true }); + expect(wrapper.getByTestId(RESPONSE_DETAILS_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getByTestId(RESPONSE_DETAILS_TEST_ID)).toHaveTextContent(PREVIEW_MESSAGE); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.tsx index 9e2ab547e9af6..8caaad7225057 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.tsx @@ -24,7 +24,7 @@ const ExtendedFlyoutWrapper = styled.div` * Automated response actions results, displayed in the document details expandable flyout left section under the Insights tab, Response tab */ export const ResponseDetails: React.FC = () => { - const { searchHit, dataAsNestedObject } = useLeftPanelContext(); + const { searchHit, dataAsNestedObject, isPreview } = useLeftPanelContext(); const endpointResponseActionsEnabled = useIsExperimentalFeatureEnabled( 'endpointResponseActionsEnabled' ); @@ -40,19 +40,28 @@ export const ResponseDetails: React.FC = () => { return (
    - -
    - -
    -
    - + {isPreview ? ( + + ) : ( + <> + +
    + +
    +
    + - - {endpointResponseActionsEnabled ? responseActionsView?.content : osqueryView?.content} - + + {endpointResponseActionsEnabled ? responseActionsView?.content : osqueryView?.content} + + + )}
    ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/context.tsx index 6dd0f65af4922..52bf509462699 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/context.tsx @@ -8,6 +8,7 @@ import type { BrowserFields, TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import React, { createContext, memo, useContext, useMemo } from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { TableId } from '@kbn/securitysolution-data-table'; import { useEventDetails } from '../shared/hooks/use_event_details'; import { FlyoutError } from '../../shared/components/flyout_error'; import { FlyoutLoading } from '../../shared/components/flyout_loading'; @@ -54,6 +55,10 @@ export interface LeftPanelContext { * Retrieves searchHit values for the provided field */ getFieldsData: GetFieldsData; + /** + * Boolean to indicate whether it is a preview flyout + */ + isPreview: boolean; } export const LeftPanelContext = createContext(undefined); @@ -97,6 +102,7 @@ export const LeftPanelProvider = memo( searchHit, investigationFields: maybeRule?.investigation_fields?.field_names ?? [], getFieldsData, + isPreview: scopeId === TableId.rulePreview, } : undefined, [ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/mocks/mock_context.ts b/x-pack/plugins/security_solution/public/flyout/document_details/left/mocks/mock_context.ts index 4233a28c1164e..4892d7d4dfa08 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/mocks/mock_context.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/mocks/mock_context.ts @@ -25,4 +25,5 @@ export const mockContextValue: LeftPanelContext = { searchHit: mockSearchHit, dataAsNestedObject: mockDataAsNestedObject, investigationFields: [], + isPreview: false, }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx index f3297b57183f3..3964f87e9bde6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx @@ -5,13 +5,13 @@ * 2.0. */ -import React, { memo, useCallback, useState, useEffect } from 'react'; +import React, { memo, useCallback } from 'react'; import { EuiButtonGroup, EuiSpacer } from '@elastic/eui'; import type { EuiButtonGroupOptionProps } from '@elastic/eui/src/components/button/button_group/button_group'; -import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { INSIGHTS_TAB_BUTTON_GROUP_TEST_ID, INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID, @@ -77,14 +77,11 @@ const insightsButtons: EuiButtonGroupOptionProps[] = [ */ export const InsightsTab: React.FC = memo(() => { const { eventId, indexName, scopeId } = useLeftPanelContext(); - const { panels, openLeftPanel } = useExpandableFlyoutContext(); - const [activeInsightsId, setActiveInsightsId] = useState( - panels.left?.path?.subTab ?? ENTITIES_TAB_ID - ); + const { openLeftPanel, panels } = useExpandableFlyoutContext(); + const activeInsightsId = panels.left?.path?.subTab ?? ENTITIES_TAB_ID; const onChangeCompressed = useCallback( (optionId: string) => { - setActiveInsightsId(optionId); openLeftPanel({ id: DocumentDetailsLeftPanelKey, path: { @@ -101,12 +98,6 @@ export const InsightsTab: React.FC = memo(() => { [eventId, indexName, scopeId, openLeftPanel] ); - useEffect(() => { - if (panels.left?.path?.subTab) { - setActiveInsightsId(panels.left?.path?.subTab); - } - }, [panels.left?.path?.subTab]); - return ( <> +const renderAnalyzerPreview = (context = panelContextValue) => render( - + @@ -117,7 +117,7 @@ describe('AnalyzerPreviewContainer', () => { ).toHaveTextContent(NO_ANALYZER_MESSAGE); }); - it('should navigate to left section Visualize tab when clicking on title', () => { + it('should navigate to analyzer in timeline when clicking on title', () => { (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); (useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({ loading: false, @@ -136,4 +136,24 @@ describe('AnalyzerPreviewContainer', () => { getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)).click(); expect(investigateInTimelineAlertClick).toHaveBeenCalled(); }); + + it('should not navigate to analyzer when in preview and clicking on title', () => { + (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); + (useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({ + loading: false, + error: false, + alertIds: ['alertid'], + statsNodes: mock.mockStatsNodes, + }); + (useInvestigateInTimeline as jest.Mock).mockReturnValue({ + investigateInTimelineAlertClick: jest.fn(), + }); + + const { queryByTestId } = renderAnalyzerPreview({ ...panelContextValue, isPreview: true }); + expect( + queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(ANALYZER_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + const { investigateInTimelineAlertClick } = useInvestigateInTimeline({}); + expect(investigateInTimelineAlertClick).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx index ac8e21d3fde06..843abeb7018e6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx @@ -27,7 +27,7 @@ const timelineId = 'timeline-1'; * Analyzer preview under Overview, Visualizations. It shows a tree representation of analyzer. */ export const AnalyzerPreviewContainer: React.FC = () => { - const { dataAsNestedObject } = useRightPanelContext(); + const { dataAsNestedObject, isPreview } = useRightPanelContext(); // decide whether to show the analyzer preview or not const isEnabled = isInvestigateInResolverActionEnabled(dataAsNestedObject); @@ -64,17 +64,18 @@ export const AnalyzerPreviewContainer: React.FC = () => { /> ), iconType: 'timeline', - ...(isEnabled && { - link: { - callback: goToAnalyzerTab, - tooltip: ( - - ), - }, - }), + ...(isEnabled && + !isPreview && { + link: { + callback: goToAnalyzerTab, + tooltip: ( + + ), + }, + }), }} data-test-subj={ANALYZER_PREVIEW_TEST_ID} > diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx index c97db50a0798b..018f0c4949af8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { TestProviders } from '../../../../common/mock'; @@ -182,7 +183,7 @@ describe('', () => { it('should navigate to the left section Insights tab when clicking on button', () => { const flyoutContextValue = { openLeftPanel: jest.fn(), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.test.tsx index 7b99e426c8abc..81d6993e805f5 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { FormattedMessage, __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { DESCRIPTION_TITLE_TEST_ID, @@ -19,6 +19,8 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import { DocumentDetailsPreviewPanelKey } from '../../preview'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/types'; +import { i18n } from '@kbn/i18n'; const ruleUuid = { category: 'kibana', @@ -46,7 +48,7 @@ const ruleName = { const flyoutContextValue = { openPreviewPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const panelContextValue = (dataFormattedForFieldBrowser: TimelineEventsDetailsItem[]) => ({ @@ -111,6 +113,15 @@ describe('', () => { expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toHaveAttribute('disabled'); }); + it('should render rule preview button as disabled if flyout is in preview', () => { + const { getByTestId } = renderDescription({ + ...panelContextValue([{ ...ruleUuid, values: [] }, ruleName, ruleDescription]), + isPreview: true, + }); + expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(RULE_SUMMARY_BUTTON_TEST_ID)).toHaveAttribute('disabled'); + }); + it('should open preview panel when clicking on button', () => { const panelContext = panelContextValue([ruleUuid, ruleDescription, ruleName]); @@ -126,11 +137,9 @@ describe('', () => { indexName: panelContext.indexName, scopeId: panelContext.scopeId, banner: { - title: ( - + title: i18n.translate( + 'xpack.securitySolution.flyout.right.about.description.rulePreviewTitle', + { defaultMessage: 'Preview rule details' } ), backgroundColor: 'warning', textColor: 'warning', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.tsx index 5c65d9231eef0..c612e2a6fb5a6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/description.tsx @@ -31,7 +31,8 @@ import { * If the document is an alert we show the rule description. If the document is of another type, we show -. */ export const Description: FC = () => { - const { dataFormattedForFieldBrowser, scopeId, eventId, indexName } = useRightPanelContext(); + const { dataFormattedForFieldBrowser, scopeId, eventId, indexName, isPreview } = + useRightPanelContext(); const { isAlert, ruleDescription, ruleName, ruleId } = useBasicDataFromDetailsData( dataFormattedForFieldBrowser ); @@ -46,11 +47,9 @@ export const Description: FC = () => { indexName, scopeId, banner: { - title: ( - + title: i18n.translate( + 'xpack.securitySolution.flyout.right.about.description.rulePreviewTitle', + { defaultMessage: 'Preview rule details' } ), backgroundColor: 'warning', textColor: 'warning', @@ -75,7 +74,7 @@ export const Description: FC = () => { defaultMessage: 'Show rule summary', } )} - disabled={isEmpty(ruleName) || isEmpty(ruleId)} + disabled={isEmpty(ruleName) || isEmpty(ruleId) || isPreview} > { ), - [ruleName, openRulePreview, ruleId] + [ruleName, openRulePreview, ruleId, isPreview] ); const alertRuleDescription = diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.test.tsx index ec7f85be212c9..1e052aa170347 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { copyToClipboard } from '@elastic/eui'; import { RightPanelContext } from '../context'; @@ -17,7 +18,7 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { TestProvidersComponent } from '../../../../common/mock'; import { useGetAlertDetailsFlyoutLink } from '../../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link'; -import { FLYOUT_URL_PARAM } from '../../shared/hooks/url/use_sync_flyout_state_with_url'; +import { URL_PARAM_KEY } from '../../../../common/hooks/use_url_state'; jest.mock('../../../../common/lib/kibana'); jest.mock('../hooks/use_assistant'); @@ -32,7 +33,7 @@ jest.mock('@elastic/eui', () => ({ })); const alertUrl = 'https://example.com/alert'; -const flyoutContextValue = {} as unknown as ExpandableFlyoutContext; +const flyoutContextValue = {} as unknown as ExpandableFlyoutContextValue; const mockContextValue = { dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, getFieldsData: jest.fn().mockImplementation(mockGetFieldsData), @@ -58,7 +59,7 @@ describe('', () => { describe('Share alert url action', () => { it('should render share button in the title and copy the the value to clipboard if document is an alert', () => { const syncedFlyoutState = 'flyoutState'; - const query = `?${FLYOUT_URL_PARAM}=${syncedFlyoutState}`; + const query = `?${URL_PARAM_KEY.eventFlyout}=${syncedFlyoutState}`; Object.defineProperty(window, 'location', { value: { @@ -73,7 +74,7 @@ describe('', () => { fireEvent.click(shareButton); expect(copyToClipboard).toHaveBeenCalledWith( - `${alertUrl}&${FLYOUT_URL_PARAM}=${syncedFlyoutState}` + `${alertUrl}&${URL_PARAM_KEY.eventFlyout}=${syncedFlyoutState}` ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.tsx index 2862aed53501b..52bcb514e7cab 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_actions.tsx @@ -10,8 +10,8 @@ import React, { memo } from 'react'; import { EuiButtonIcon, EuiCopy, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { NewChatById } from '@kbn/elastic-assistant'; +import { URL_PARAM_KEY } from '../../../../common/hooks/use_url_state'; import { copyFunction } from '../../../shared/utils/copy_to_clipboard'; -import { FLYOUT_URL_PARAM } from '../../shared/hooks/url/use_sync_flyout_state_with_url'; import { useGetAlertDetailsFlyoutLink } from '../../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link'; import { useBasicDataFromDetailsData } from '../../../../timelines/components/side_panel/event_details/helpers'; import { useAssistant } from '../hooks/use_assistant'; @@ -39,7 +39,7 @@ export const HeaderActions: VFC = memo(() => { const modifier = (value: string) => { const query = new URLSearchParams(window.location.search); - return `${value}&${FLYOUT_URL_PARAM}=${query.get(FLYOUT_URL_PARAM)}`; + return `${value}&${URL_PARAM_KEY.eventFlyout}=${query.get(URL_PARAM_KEY.eventFlyout)}`; }; const { showAssistant, promptContextId } = useAssistant({ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.test.tsx index f192a84211a47..9c3157f9d5d0a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.test.tsx @@ -7,12 +7,14 @@ import React from 'react'; import { render } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { RISK_SCORE_VALUE_TEST_ID, SEVERITY_VALUE_TEST_ID, FLYOUT_HEADER_TITLE_TEST_ID, + STATUS_BUTTON_TEST_ID, } from './test_ids'; import { HeaderTitle } from './header_title'; import moment from 'moment-timezone'; @@ -27,7 +29,7 @@ moment.suppressDeprecationWarnings = true; moment.tz.setDefault('UTC'); const dateFormat = 'MMM D, YYYY @ HH:mm:ss.SSS'; -const flyoutContextValue = {} as unknown as ExpandableFlyoutContext; +const flyoutContextValue = {} as unknown as ExpandableFlyoutContextValue; const mockContextValue = { dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, getFieldsData: jest.fn().mockImplementation(mockGetFieldsData), @@ -56,6 +58,7 @@ describe('', () => { expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument(); expect(getByTestId(RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument(); expect(getByTestId(SEVERITY_VALUE_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(STATUS_BUTTON_TEST_ID)).toBeInTheDocument(); }); it('should render rule name in the title if document is an alert', () => { @@ -82,4 +85,32 @@ describe('', () => { expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('Event details'); }); + + it('should not render document status if document is not an alert', () => { + const contextValue = { + ...mockContextValue, + dataFormattedForFieldBrowser: [ + { + category: 'kibana', + field: 'kibana.alert.rule.name', + values: [], + originalValue: [], + isObjectArray: false, + }, + ], + } as unknown as RightPanelContext; + + const { queryByTestId } = renderHeader(contextValue); + expect(queryByTestId(STATUS_BUTTON_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should not render document status if flyout is open in preview', () => { + const contextValue = { + ...mockContextValue, + isPreview: true, + } as unknown as RightPanelContext; + + const { queryByTestId } = renderHeader(contextValue); + expect(queryByTestId(STATUS_BUTTON_TEST_ID)).not.toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.tsx index ac52136e3afae..c8dfa6e847412 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/header_title.tsx @@ -25,33 +25,40 @@ import { FlyoutTitle } from '../../../shared/components/flyout_title'; * Document details flyout right section header */ export const HeaderTitle: FC = memo(() => { - const { dataFormattedForFieldBrowser, eventId, scopeId } = useRightPanelContext(); + const { dataFormattedForFieldBrowser, eventId, scopeId, isPreview } = useRightPanelContext(); const { isAlert, ruleName, timestamp, ruleId } = useBasicDataFromDetailsData( dataFormattedForFieldBrowser ); const ruleTitle = useMemo( - () => ( - + () => + isPreview ? ( - - ), - [ruleName, ruleId, eventId, scopeId] + ) : ( + + + + ), + [ruleName, ruleId, eventId, scopeId, isPreview] ); const eventTitle = ( @@ -76,9 +83,11 @@ export const HeaderTitle: FC = memo(() => {
    - - - + {isAlert && !isPreview && ( + + + + )} diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx index 717cf9856651e..806a43f9f5497 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx @@ -15,6 +15,7 @@ import { convertHighlightedFieldsToTableRow } from '../../shared/utils/highlight import { useRuleWithFallback } from '../../../../detection_engine/rule_management/logic/use_rule_with_fallback'; import { useBasicDataFromDetailsData } from '../../../../timelines/components/side_panel/event_details/helpers'; import { HighlightedFieldsCell } from './highlighted_fields_cell'; +import { SecurityCellActionType } from '../../../../actions/constants'; import { CellActionsMode, SecurityCellActions, @@ -42,6 +43,10 @@ export interface HighlightedFieldsTableRow { * Maintain backwards compatibility // TODO remove when possible */ scopeId: string; + /** + * Boolean to indicate this field is shown in a preview + */ + isPreview: boolean; }; } @@ -71,6 +76,7 @@ const columns: Array> = [ field: string; values: string[] | null | undefined; scopeId: string; + isPreview: boolean; }) => ( > = [ visibleCellActions={6} sourcererScopeId={getSourcererScopeId(description.scopeId)} metadata={{ scopeId: description.scopeId }} + disabledActionTypes={ + description.isPreview + ? [SecurityCellActionType.FILTER, SecurityCellActionType.TOGGLE_COLUMN] + : [] + } > @@ -93,7 +104,7 @@ const columns: Array> = [ * Component that displays the highlighted fields in the right panel under the Investigation section. */ export const HighlightedFields: FC = () => { - const { dataFormattedForFieldBrowser, scopeId } = useRightPanelContext(); + const { dataFormattedForFieldBrowser, scopeId, isPreview } = useRightPanelContext(); const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); const { loading, rule: maybeRule } = useRuleWithFallback(ruleId); @@ -102,8 +113,8 @@ export const HighlightedFields: FC = () => { investigationFields: maybeRule?.investigation_fields?.field_names ?? [], }); const items = useMemo( - () => convertHighlightedFieldsToTableRow(highlightedFields, scopeId), - [highlightedFields, scopeId] + () => convertHighlightedFieldsToTableRow(highlightedFields, scopeId, isPreview), + [highlightedFields, scopeId, isPreview] ); return ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx index c56fef68a8bfa..0112e06cb489f 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_cell.test.tsx @@ -13,6 +13,7 @@ import { HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID, } from './test_ids'; import { HighlightedFieldsCell } from './highlighted_fields_cell'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left'; @@ -24,7 +25,7 @@ jest.mock('../../../../management/hooks'); const flyoutContextValue = { openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const panelContextValue = { eventId: 'event id', indexName: 'indexName', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx index 31a86495d0561..36fb3731943c4 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx @@ -21,6 +21,7 @@ import { import { RightPanelContext } from '../context'; import { mockContextValue } from '../mocks/mock_context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left'; import { ENTITIES_TAB_ID } from '../../left/components/entities_details'; @@ -42,7 +43,7 @@ const panelContextValue = { const flyoutContextValue = { openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); jest.mock('../../../../common/containers/use_global_time', () => { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.test.tsx index f774fe67e179b..d2444248d4202 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.test.tsx @@ -24,6 +24,7 @@ import { LeftPanelInvestigationTab, DocumentDetailsLeftPanelKey } from '../../le jest.mock('../../shared/hooks/use_investigation_guide'); const NO_DATA_MESSAGE = 'Investigation guideThere’s no investigation guide for this rule.'; +const PREVIEW_MESSAGE = 'Investigation guide is not available in alert preview.'; const renderInvestigationGuide = () => render( @@ -97,6 +98,21 @@ describe('', () => { expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(NO_DATA_MESSAGE); }); + it('should render preview message when flyout is in preview', () => { + const { queryByTestId, getByTestId } = render( + + + + + + + + ); + + expect(queryByTestId(INVESTIGATION_GUIDE_BUTTON_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(INVESTIGATION_GUIDE_TEST_ID)).toHaveTextContent(PREVIEW_MESSAGE); + }); + it('should navigate to investigation guide when clicking on button', () => { (useInvestigationGuide as jest.Mock).mockReturnValue({ loading: false, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.tsx index 04c73baad9d78..427a9987de870 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/investigation_guide.tsx @@ -24,7 +24,8 @@ import { */ export const InvestigationGuide: React.FC = () => { const { openLeftPanel } = useExpandableFlyoutContext(); - const { eventId, indexName, scopeId, dataFormattedForFieldBrowser } = useRightPanelContext(); + const { eventId, indexName, scopeId, dataFormattedForFieldBrowser, isPreview } = + useRightPanelContext(); const { loading, error, basicAlertData, ruleNote } = useInvestigationGuide({ dataFormattedForFieldBrowser, @@ -56,7 +57,12 @@ export const InvestigationGuide: React.FC = () => {

    - {loading ? ( + {isPreview ? ( + + ) : loading ? ( d.field !== 'kibana.alert.rule.type' diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.test.tsx index fe711387dbf17..5bdca0ec3dfa2 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/prevalence_overview.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { render } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock'; @@ -35,7 +36,7 @@ const NO_DATA_MESSAGE = 'No prevalence data available.'; const flyoutContextValue = { openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const renderPrevalenceOverview = (contextValue: RightPanelContext = mockContextValue) => render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.test.tsx index 65a241a00e6bb..9b459acbaf327 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { FormattedMessage, __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { REASON_DETAILS_PREVIEW_BUTTON_TEST_ID, REASON_TITLE_TEST_ID } from './test_ids'; import { Reason } from './reason'; import { RightPanelContext } from '../context'; @@ -15,10 +15,12 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { DocumentDetailsPreviewPanelKey } from '../../preview'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/types'; +import { i18n } from '@kbn/i18n'; const flyoutContextValue = { openPreviewPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const panelContextValue = { eventId: 'event id', @@ -89,11 +91,11 @@ describe('', () => { indexName: panelContextValue.indexName, scopeId: panelContextValue.scopeId, banner: { - title: ( - + title: i18n.translate( + 'xpack.securitySolution.flyout.right.about.reason.alertReasonPreviewTitle', + { + defaultMessage: 'Preview alert reason', + } ), backgroundColor: 'warning', textColor: 'warning', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx index 392e753213714..96ec5b2b0232c 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx @@ -41,11 +41,11 @@ export const Reason: FC = () => { indexName, scopeId, banner: { - title: ( - + title: i18n.translate( + 'xpack.securitySolution.flyout.right.about.reason.alertReasonPreviewTitle', + { + defaultMessage: 'Preview alert reason', + } ), backgroundColor: 'warning', textColor: 'warning', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.test.tsx index ebff6c0e704fe..d521155ea1631 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.test.tsx @@ -10,10 +10,13 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { RESPONSE_SECTION_CONTENT_TEST_ID, RESPONSE_SECTION_HEADER_TEST_ID } from './test_ids'; import { RightPanelContext } from '../context'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { ResponseSection } from './response_section'; -const flyoutContextValue = {} as unknown as ExpandableFlyoutContext; +const PREVIEW_MESSAGE = 'Response is not available in alert preview.'; + +const flyoutContextValue = {} as unknown as ExpandableFlyoutContextValue; const panelContextValue = {} as unknown as RightPanelContext; const renderResponseSection = () => @@ -47,4 +50,18 @@ describe('', () => { getByTestId(RESPONSE_SECTION_HEADER_TEST_ID).click(); expect(getByTestId(RESPONSE_SECTION_CONTENT_TEST_ID)).toBeInTheDocument(); }); + + it('should render preview message if flyout is in preview', () => { + const { getByTestId } = render( + + + + + + + + ); + getByTestId(RESPONSE_SECTION_HEADER_TEST_ID).click(); + expect(getByTestId(RESPONSE_SECTION_CONTENT_TEST_ID)).toHaveTextContent(PREVIEW_MESSAGE); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.tsx index 96a0b070020e2..6b7ca6e282246 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/response_section.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { ResponseButton } from './response_button'; import { ExpandableSection } from './expandable_section'; +import { useRightPanelContext } from '../context'; import { RESPONSE_SECTION_TEST_ID } from './test_ids'; export interface ResponseSectionProps { /** @@ -22,6 +23,7 @@ export interface ResponseSectionProps { * Most bottom section of the overview tab. It contains a summary of the response tab. */ export const ResponseSection: VFC = ({ expanded = false }) => { + const { isPreview } = useRightPanelContext(); return ( = ({ expanded = false }) } data-test-subj={RESPONSE_SECTION_TEST_ID} > - + {isPreview ? ( + + ) : ( + + )} ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.test.tsx index 775e195e764fd..525ca03dbc045 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.test.tsx @@ -10,6 +10,7 @@ import { useProcessData } from '../hooks/use_process_data'; import { SessionPreview } from './session_preview'; import { TestProviders } from '../../../../common/mock'; import React from 'react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; @@ -17,7 +18,7 @@ jest.mock('../hooks/use_process_data'); const flyoutContextValue = { openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const panelContextValue = { eventId: 'event id', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.tsx index 63f07cb7ab1f3..08c15480bb7ed 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview.tsx @@ -37,7 +37,7 @@ const ValueContainer: FC<{ text?: ReactElement }> = ({ text, children }) => ( * Renders session preview under Visualizations section in the flyout right EuiPanel */ export const SessionPreview: FC = () => { - const { eventId, scopeId } = useRightPanelContext(); + const { eventId, scopeId, isPreview } = useRightPanelContext(); const { processName, userName, startAt, ruleName, ruleId, workdir, command } = useProcessData(); const { euiTheme } = useEuiTheme(); @@ -100,13 +100,13 @@ export const SessionPreview: FC = () => { fieldType={'string'} isAggregatable={false} isDraggable={false} - linkValue={ruleId} + linkValue={!isPreview ? ruleId : null} value={ruleName} /> ) ); - }, [ruleName, ruleId, scopeId, eventId]); + }, [ruleName, ruleId, scopeId, eventId, isPreview]); const commandFragment = useMemo(() => { return ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx index cfd5bcc525700..80d9c81a064e8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx @@ -40,10 +40,10 @@ const sessionViewConfig = { sessionStartTime: 'sessionStartTime', }; -const renderSessionPreview = () => +const renderSessionPreview = (context = panelContextValue) => render( - + @@ -121,4 +121,31 @@ describe('SessionPreviewContainer', () => { ).not.toHaveTextContent(NO_DATA_MESSAGE); expect(queryByTestId(SESSION_PREVIEW_TEST_ID)).not.toBeInTheDocument(); }); + + it('should not render link to session viewer if flyout is open in preview', () => { + (useSessionPreview as jest.Mock).mockReturnValue(sessionViewConfig); + (useLicense as jest.Mock).mockReturnValue({ isEnterprise: () => true }); + + const { getByTestId, queryByTestId } = renderSessionPreview({ + ...panelContextValue, + isPreview: true, + }); + + expect(getByTestId(SESSION_PREVIEW_TEST_ID)).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(SESSION_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_CONTENT_TEST_ID(SESSION_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + getByTestId(EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(SESSION_PREVIEW_TEST_ID)) + ).toBeInTheDocument(); + expect( + queryByTestId(EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(SESSION_PREVIEW_TEST_ID)) + ).not.toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx index 10250e74c383c..d0214b725a44e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx @@ -29,7 +29,7 @@ const timelineId = 'timeline-1'; * Checks if the SessionView component is available, if so render it or else render an error message */ export const SessionPreviewContainer: FC = () => { - const { dataAsNestedObject, getFieldsData } = useRightPanelContext(); + const { dataAsNestedObject, getFieldsData, isPreview } = useRightPanelContext(); // decide whether to show the session view or not const sessionViewConfig = useSessionPreview({ getFieldsData }); @@ -122,17 +122,18 @@ export const SessionPreviewContainer: FC = () => { /> ), iconType: 'timeline', - ...(isEnabled && { - link: { - callback: goToSessionViewTab, - tooltip: ( - - ), - }, - }), + ...(isEnabled && + !isPreview && { + link: { + callback: goToSessionViewTab, + tooltip: ( + + ), + }, + }), }} data-test-subj={SESSION_PREVIEW_TEST_ID} > diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.test.tsx index 0b52e0ef67665..1c9412deab05a 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { DocumentStatus } from './status'; @@ -19,7 +20,7 @@ jest.mock('../../../../detections/components/alerts_table/timeline_actions/use_a const flyoutContextValue = { closeFlyout: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const renderStatus = (contextValue: RightPanelContext) => render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.test.tsx index f5ae35db72f16..d752df3d9350e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/threat_intelligence_overview.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { TestProviders } from '../../../../common/mock'; @@ -147,7 +148,7 @@ describe('', () => { }); const flyoutContextValue = { openLeftPanel: jest.fn(), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx index 155f2c127fc3c..c8673f41376f4 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx @@ -20,6 +20,7 @@ import { import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details'; import { mockContextValue } from '../mocks/mock_context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { RightPanelContext } from '../context'; import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left'; @@ -42,7 +43,7 @@ const panelContextValue = { const flyoutContextValue = { openLeftPanel: jest.fn(), -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); jest.mock('../../../../common/containers/use_global_time', () => { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx index 14a0136c73ed9..8133dfa528526 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/visualizations_section.test.tsx @@ -15,6 +15,7 @@ import { mockContextValue } from '../mocks/mock_context'; import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser'; import { RightPanelContext } from '../context'; import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; jest.mock('../../../../common/containers/alerts/use_alert_prevalence_from_process_tree', () => ({ @@ -40,7 +41,7 @@ describe('', () => { it('should render visualizations component', () => { const flyoutContextValue = { openLeftPanel: jest.fn(), - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId, getAllByRole } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/context.tsx index b46645aaf883c..7311a030b2175 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/context.tsx @@ -8,6 +8,7 @@ import type { BrowserFields, TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import React, { createContext, memo, useContext, useMemo } from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { TableId } from '@kbn/securitysolution-data-table'; import { useEventDetails } from '../shared/hooks/use_event_details'; import { FlyoutError } from '../../shared/components/flyout_error'; @@ -59,6 +60,10 @@ export interface RightPanelContext { * Retrieves searchHit values for the provided field */ getFieldsData: GetFieldsData; + /** + * Boolean to indicate whether it is a preview flyout + */ + isPreview: boolean; } export const RightPanelContext = createContext(undefined); @@ -104,6 +109,7 @@ export const RightPanelProvider = memo( investigationFields: maybeRule?.investigation_fields?.field_names ?? [], refetchFlyoutData, getFieldsData, + isPreview: scopeId === TableId.rulePreview, } : undefined, [ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx index 49ad491efa4d1..995beea63a70f 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx @@ -15,10 +15,17 @@ import { useHostIsolationTools } from '../../../timelines/components/side_panel/ import { DEFAULT_DARK_MODE } from '../../../../common/constants'; import { useUiSetting } from '../../../common/lib/kibana'; +interface PanelFooterProps { + /** + * Boolean that indicates whether flyout is in preview and action should be hidden + */ + isPreview: boolean; +} + /** * */ -export const PanelFooter: FC = () => { +export const PanelFooter: FC = ({ isPreview }) => { const { closeFlyout, openRightPanel } = useExpandableFlyoutContext(); const { eventId, @@ -48,7 +55,7 @@ export const PanelFooter: FC = () => { [eventId, indexName, openRightPanel, scopeId, showHostIsolationPanel] ); - return ( + return !isPreview ? ( { refetchFlyoutData={refetchFlyoutData} /> - ); + ) : null; }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx index 39abe1f818a96..08142a0ef08ba 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx @@ -37,7 +37,7 @@ export interface RightPanelProps extends FlyoutPanelProps { */ export const RightPanel: FC> = memo(({ path }) => { const { openRightPanel } = useExpandableFlyoutContext(); - const { eventId, getFieldsData, indexName, scopeId } = useRightPanelContext(); + const { eventId, getFieldsData, indexName, scopeId, isPreview } = useRightPanelContext(); // for 8.10, we only render the flyout in its expandable mode if the document viewed is of type signal const documentIsSignal = getField(getFieldsData('event.kind')) === EventKind.signal; @@ -72,7 +72,7 @@ export const RightPanel: FC> = memo(({ path }) => { setSelectedTabId={setSelectedTabId} /> - + ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/mocks/mock_context.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/mocks/mock_context.ts index 2bb599a657591..086c272bee359 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/mocks/mock_context.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/mocks/mock_context.ts @@ -26,4 +26,5 @@ export const mockContextValue: RightPanelContext = { searchHit: mockSearchHit, investigationFields: [], refetchFlyoutData: jest.fn(), + isPreview: false, }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/json_tab.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/json_tab.tsx index 6d329811f7228..313e8f952e1a3 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/json_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/json_tab.tsx @@ -23,7 +23,7 @@ const FLYOUT_FOOTER_HEIGHT = 72; * Json view displayed in the document details expandable flyout right section */ export const JsonTab: FC = memo(() => { - const { searchHit } = useRightPanelContext(); + const { searchHit, isPreview } = useRightPanelContext(); const jsonValue = JSON.stringify(searchHit, null, 2); const flexGroupElement = useRef(null); @@ -31,19 +31,20 @@ export const JsonTab: FC = memo(() => { useEffect(() => { const topPosition = flexGroupElement?.current?.getBoundingClientRect().top || 0; + const footerOffset = isPreview ? 0 : FLYOUT_FOOTER_HEIGHT; const height = window.innerHeight - topPosition - COPY_TO_CLIPBOARD_BUTTON_HEIGHT - FLYOUT_BODY_PADDING - - FLYOUT_FOOTER_HEIGHT; + footerOffset; if (height === 0) { return; } setEditorHeight(height); - }, [setEditorHeight]); + }, [setEditorHeight, isPreview]); return ( ; - -export const SecuritySolutionFlyoutCloseContext = createContext< - SecuritySolutionFlyoutCloseContextValue | undefined ->(undefined); - -/** - * Exposes the flyout close context value (returned from syncUrl) as a hook. - */ -export const useSecurityFlyoutUrlSync = () => { - const contextValue = useContext(SecuritySolutionFlyoutCloseContext); - - if (!contextValue) { - throw new Error('useSecurityFlyoutUrlSync can only be used inside respective provider'); - } - - return contextValue; -}; - -/** - * Provides urlSync hook return value as a context value, for reuse in other components. - * Main goal here is to avoid calling useSyncFlyoutStateWithUrl multiple times. - */ -export const SecuritySolutionFlyoutUrlSyncProvider: FC = ({ children }) => { - const [flyoutRef, handleFlyoutChangedOrClosed] = useSyncFlyoutStateWithUrl(); - - const value: SecuritySolutionFlyoutCloseContextValue = useMemo( - () => [flyoutRef, handleFlyoutChangedOrClosed], - [flyoutRef, handleFlyoutChangedOrClosed] - ); - - return ( - - {children} - - ); -}; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/expandable_flyout_state_from_event_meta.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/expandable_flyout_state_from_event_meta.ts index e25824ed8b68a..d80d6f5983b89 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/expandable_flyout_state_from_event_meta.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/expandable_flyout_state_from_event_meta.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { ExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { DocumentDetailsRightPanelKey } from '../../../right'; interface RedirectParams { @@ -19,11 +18,7 @@ interface RedirectParams { * This value can be used to open the flyout either by passing it directly to the flyout api (exposed via ref) or * by serializing it to the url & performing a redirect */ -export const expandableFlyoutStateFromEventMeta = ({ - index, - eventId, - scopeId, -}: RedirectParams): ExpandableFlyoutContext['panels'] => { +export const expandableFlyoutStateFromEventMeta = ({ index, eventId, scopeId }: RedirectParams) => { return { right: { id: DocumentDetailsRightPanelKey, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.test.tsx deleted file mode 100644 index 984b2a2e223dc..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.test.tsx +++ /dev/null @@ -1,77 +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 { ExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { useSyncToUrl } from '@kbn/url-state'; -import { renderHook } from '@testing-library/react-hooks'; -import { useSyncFlyoutStateWithUrl } from './use_sync_flyout_state_with_url'; - -jest.mock('@kbn/url-state'); - -describe('useSyncFlyoutStateWithUrl', () => { - it('should return an array containing flyoutApi ref and handleFlyoutChanges function', () => { - const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); - const [flyoutApi, handleFlyoutChanges] = result.current; - - expect(flyoutApi.current).toBeNull(); - expect(typeof handleFlyoutChanges).toBe('function'); - }); - - it('should open flyout when relevant url state is detected in the query string', () => { - jest.useFakeTimers(); - - jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { - setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); - return jest.fn(); - }); - - const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); - const [flyoutApi, handleFlyoutChanges] = result.current; - - const flyoutApiMock: ExpandableFlyoutApi = { - openFlyout: jest.fn(), - getState: () => ({ left: undefined, right: undefined, preview: [] }), - }; - - expect(typeof handleFlyoutChanges).toBe('function'); - expect(flyoutApi.current).toBeNull(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (flyoutApi as any).current = flyoutApiMock; - - jest.runOnlyPendingTimers(); - jest.useRealTimers(); - - expect(flyoutApiMock.openFlyout).toHaveBeenCalledTimes(1); - expect(flyoutApiMock.openFlyout).toHaveBeenCalledWith({ mocked: { flyout: 'state' } }); - }); - - it('should sync flyout state to url whenever handleFlyoutChanges is called by the consumer', () => { - const syncStateToUrl = jest.fn(); - jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { - setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); - return syncStateToUrl; - }); - - const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); - const [_flyoutApi, handleFlyoutChanges] = result.current; - - handleFlyoutChanges(); - - expect(syncStateToUrl).toHaveBeenCalledTimes(1); - expect(syncStateToUrl).toHaveBeenLastCalledWith(undefined); - - handleFlyoutChanges({ left: undefined, right: undefined, preview: [] }); - - expect(syncStateToUrl).toHaveBeenLastCalledWith({ - left: undefined, - right: undefined, - preview: undefined, - }); - expect(syncStateToUrl).toHaveBeenCalledTimes(2); - }); -}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.tsx deleted file mode 100644 index 97e2500f3f948..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/url/use_sync_flyout_state_with_url.tsx +++ /dev/null @@ -1,55 +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 { useCallback, useRef } from 'react'; -import type { ExpandableFlyoutApi, ExpandableFlyoutContext } from '@kbn/expandable-flyout'; -import { useSyncToUrl } from '@kbn/url-state'; -import last from 'lodash/last'; -import { URL_PARAM_KEY } from '../../../../../common/hooks/use_url_state'; - -export const FLYOUT_URL_PARAM = URL_PARAM_KEY.eventFlyout; - -type FlyoutState = Parameters[0]; - -/** - * Sync flyout state with the url and open it when relevant url state is detected in the query string - * @returns [ref, flyoutChangesHandler] - */ -export const useSyncFlyoutStateWithUrl = () => { - const flyoutApi = useRef(null); - - const handleRestoreFlyout = useCallback( - (state?: FlyoutState) => { - if (!state) { - return; - } - - flyoutApi.current?.openFlyout(state); - }, - [flyoutApi] - ); - - const syncStateToUrl = useSyncToUrl(FLYOUT_URL_PARAM, handleRestoreFlyout); - - // This should be bound to flyout changed and closed events. - // When flyout is closed, url state is cleared - const handleFlyoutChanges = useCallback( - (state?: ExpandableFlyoutContext['panels']) => { - if (!state) { - return syncStateToUrl(undefined); - } - - return syncStateToUrl({ - ...state, - preview: last(state.preview), - }); - }, - [syncStateToUrl] - ); - - return [flyoutApi, handleFlyoutChanges] as const; -}; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.test.tsx index 00a25ed1885aa..485742aef982e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.test.tsx @@ -7,13 +7,32 @@ import { renderHook } from '@testing-library/react-hooks'; -import { useGetUserCasesPermissions } from '../../../../common/lib/kibana'; +import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; import { useShowRelatedCases } from './use_show_related_cases'; -jest.mock('../../../../common/lib/kibana'); + +const mockedUseKibana = mockUseKibana(); +const mockCanUseCases = jest.fn(); + +jest.mock('../../../../common/lib/kibana/kibana_react', () => { + const original = jest.requireActual('../../../../common/lib/kibana/kibana_react'); + + return { + ...original, + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + cases: { + helpers: { canUseCases: mockCanUseCases }, + }, + }, + }), + }; +}); describe('useShowRelatedCases', () => { it(`should return false if user doesn't have cases read privilege`, () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue({ + mockCanUseCases.mockReturnValue({ all: false, create: false, read: false, @@ -28,7 +47,7 @@ describe('useShowRelatedCases', () => { }); it('should return true if user has cases read privilege', () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue({ + mockCanUseCases.mockReturnValue({ all: false, create: false, read: true, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.ts index e469cc2ef155c..7bc3429cd0585 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_cases.ts @@ -5,12 +5,15 @@ * 2.0. */ -import { useGetUserCasesPermissions } from '../../../../common/lib/kibana'; +import { APP_ID } from '../../../../../common'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; /** * Returns true if the user has read privileges for cases, false otherwise */ export const useShowRelatedCases = (): boolean => { - const userCasesPermissions = useGetUserCasesPermissions(); + const { cases } = useKibana().services; + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); + return userCasesPermissions.read; }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/mocks/mock_flyout_context.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/mocks/mock_flyout_context.ts index b579a68994604..419047bc30f77 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/mocks/mock_flyout_context.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/mocks/mock_flyout_context.ts @@ -5,14 +5,12 @@ * 2.0. */ -import type { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; -import type { State } from '@kbn/expandable-flyout/src/reducer'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; /** * Mock flyout context */ -export const mockFlyoutContextValue: ExpandableFlyoutContext = { - panels: {} as State, +export const mockFlyoutContextValue: ExpandableFlyoutContextValue = { openFlyout: jest.fn(), openRightPanel: jest.fn(), openLeftPanel: jest.fn(), @@ -22,4 +20,9 @@ export const mockFlyoutContextValue: ExpandableFlyoutContext = { closePreviewPanel: jest.fn(), previousPreviewPanel: jest.fn(), closeFlyout: jest.fn(), + panels: { + left: undefined, + right: undefined, + preview: [], + }, }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.test.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.test.ts index 3992966878192..1565837f90fc2 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.test.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.test.ts @@ -11,6 +11,7 @@ import { } from './highlighted_fields_helpers'; const scopeId = 'scopeId'; +const isPreview = false; describe('convertHighlightedFieldsToTableRow', () => { it('should convert highlighted fields to a table row', () => { @@ -19,13 +20,14 @@ describe('convertHighlightedFieldsToTableRow', () => { values: ['host-1'], }, }; - expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId)).toEqual([ + expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId, isPreview)).toEqual([ { field: 'host.name', description: { field: 'host.name', values: ['host-1'], scopeId: 'scopeId', + isPreview, }, }, ]); @@ -38,13 +40,14 @@ describe('convertHighlightedFieldsToTableRow', () => { values: ['host-1'], }, }; - expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId)).toEqual([ + expect(convertHighlightedFieldsToTableRow(highlightedFields, scopeId, isPreview)).toEqual([ { field: 'host.name-override', description: { field: 'host.name-override', values: ['host-1'], scopeId: 'scopeId', + isPreview, }, }, ]); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.ts index d41ff1b75f28a..6cf1ec9291efe 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/utils/highlighted_fields_helpers.ts @@ -16,7 +16,8 @@ import type { HighlightedFieldsTableRow } from '../../right/components/highlight */ export const convertHighlightedFieldsToTableRow = ( highlightedFields: UseHighlightedFieldsResult, - scopeId: string + scopeId: string, + isPreview: boolean ): HighlightedFieldsTableRow[] => { const fieldNames = Object.keys(highlightedFields); return fieldNames.map((fieldName) => { @@ -30,6 +31,7 @@ export const convertHighlightedFieldsToTableRow = ( field, values, scopeId, + isPreview, }, }; }); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx index e45869115c50f..7c12607919cf7 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx @@ -7,6 +7,7 @@ import React from 'react'; import type { Story } from '@storybook/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { StorybookProviders } from '../../../../common/mock/storybook_providers'; import { mockRiskScoreState } from '../../../../timelines/components/side_panel/new_user_detail/__mocks__'; @@ -20,7 +21,7 @@ export default { const flyoutContextValue = { openLeftPanel: () => window.alert('openLeftPanel called'), panels: {}, -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; export const Default: Story = () => { return ( diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.stories.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.stories.tsx index 746aa8b25e0f1..b4555969dd41c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.stories.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { EuiFlyout } from '@elastic/eui'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { StorybookProviders } from '../../../common/mock/storybook_providers'; import { @@ -20,7 +21,7 @@ import { UserPanelContent } from './content'; const flyoutContextValue = { openLeftPanel: () => window.alert('openLeftPanel called'), panels: {}, -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; storiesOf('Components/UserPanelContent', module) .addDecorator((storyFn) => ( diff --git a/x-pack/plugins/security_solution/public/flyout/index.tsx b/x-pack/plugins/security_solution/public/flyout/index.tsx index 1c8ac6c4cc2c9..a9194a3a766c6 100644 --- a/x-pack/plugins/security_solution/public/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/index.tsx @@ -23,10 +23,6 @@ import { RightPanelProvider } from './document_details/right/context'; import type { LeftPanelProps } from './document_details/left'; import { LeftPanel, DocumentDetailsLeftPanelKey } from './document_details/left'; import { LeftPanelProvider } from './document_details/left/context'; -import { - SecuritySolutionFlyoutUrlSyncProvider, - useSecurityFlyoutUrlSync, -} from './document_details/shared/context/url_sync'; import type { PreviewPanelProps } from './document_details/preview'; import { PreviewPanel, DocumentDetailsPreviewPanelKey } from './document_details/preview'; import { PreviewPanelProvider } from './document_details/preview/context'; @@ -34,7 +30,6 @@ import type { UserPanelExpandableFlyoutProps } from './entity_details/user_right import { UserPanel, UserPanelKey } from './entity_details/user_right'; import type { RiskInputsExpandableFlyoutProps } from './entity_details/risk_inputs_left'; import { RiskInputsPanel, RiskInputsPanelKey } from './entity_details/risk_inputs_left'; - /** * List of all panels that will be used within the document details expandable flyout. * This needs to be passed to the expandable flyout registeredPanels property. @@ -84,42 +79,15 @@ const expandableFlyoutDocumentsPanels: ExpandableFlyoutProps['registeredPanels'] }, ]; -const OuterProviders: FC = ({ children }) => { - return {children}; -}; - -const InnerProviders: FC = ({ children }) => { - const [flyoutRef, handleFlyoutChangedOrClosed] = useSecurityFlyoutUrlSync(); - - return ( - - {children} - - ); -}; - +// NOTE: provider below accepts "storage" prop, please take a look into component's JSDoc. export const SecuritySolutionFlyoutContextProvider: FC = ({ children }) => ( - - {children} - + {children} ); SecuritySolutionFlyoutContextProvider.displayName = 'SecuritySolutionFlyoutContextProvider'; -export const SecuritySolutionFlyout = memo(() => { - const [_flyoutRef, handleFlyoutChangedOrClosed] = useSecurityFlyoutUrlSync(); - - return ( - - ); -}); +export const SecuritySolutionFlyout = memo(() => ( + +)); SecuritySolutionFlyout.displayName = 'SecuritySolutionFlyout'; diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx index cf8d82ff251a5..e41bf0c347b45 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.stories.tsx @@ -7,6 +7,7 @@ import React from 'react'; import type { Story } from '@storybook/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { EuiButtonIcon } from '@elastic/eui'; import { FlyoutNavigation } from './flyout_navigation'; @@ -21,7 +22,7 @@ export default { const flyoutContextValue = { closeLeftPanel: () => window.alert('close left panel'), panels: {}, -} as unknown as ExpandableFlyoutContext; +} as unknown as ExpandableFlyoutContextValue; export const Expand: Story = () => { return ( @@ -38,7 +39,7 @@ export const Collapse: Story = () => { { ...flyoutContextValue, panels: { left: {} }, - } as unknown as ExpandableFlyoutContext + } as unknown as ExpandableFlyoutContextValue } > diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx index 9972f0e90f350..7642dfcfd81e3 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { act, render } from '@testing-library/react'; +import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context'; import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; import { TestProviders } from '../../../common/mock'; import { FlyoutNavigation } from './flyout_navigation'; @@ -24,7 +25,7 @@ describe('', () => { it('should render expand button', () => { const flyoutContextValue = { panels: {}, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId, queryByTestId } = render( @@ -47,7 +48,7 @@ describe('', () => { panels: { left: {}, }, - } as unknown as ExpandableFlyoutContext; + } as unknown as ExpandableFlyoutContextValue; const { getByTestId, queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx index 820b0ac5efcd9..cc969988a16a4 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/flyout_navigation.tsx @@ -48,7 +48,7 @@ export const FlyoutNavigation: FC = memo( const { euiTheme } = useEuiTheme(); const { closeLeftPanel, panels } = useExpandableFlyoutContext(); - const isExpanded: boolean = panels.left != null; + const isExpanded: boolean = !!panels.left; const collapseDetails = useCallback(() => closeLeftPanel(), [closeLeftPanel]); const collapseButton = useMemo( diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx index ac7422619ee07..d872eee2d2cee 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx @@ -17,6 +17,7 @@ import { OS_WINDOWS, CONDITION_AND, CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES, + CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH, CONDITION_OPERATOR_TYPE_NESTED, CONDITION_OPERATOR_TYPE_MATCH, CONDITION_OPERATOR_TYPE_MATCH_ANY, @@ -47,6 +48,7 @@ const OPERATOR_TYPE_LABELS_INCLUDED = Object.freeze({ const OPERATOR_TYPE_LABELS_EXCLUDED = Object.freeze({ [ListOperatorTypeEnum.MATCH_ANY]: CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY, [ListOperatorTypeEnum.MATCH]: CONDITION_OPERATOR_TYPE_NOT_MATCH, + [ListOperatorTypeEnum.WILDCARD]: CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH, }); const EuiFlexGroupNested = styled(EuiFlexGroup)` diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts index 273cda46aa721..2024541091d57 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts @@ -68,6 +68,13 @@ export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate( } ); +export const CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.wildcardDoesNotMatchOperator', + { + defaultMessage: 'DOES NOT MATCH', + } +); + export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( 'xpack.securitySolution.artifactCard.conditions.nestedOperator', { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts index 4a80c693beb59..b6040691c485f 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts @@ -59,8 +59,7 @@ const visitArtifactTab = (tabId: string) => { cy.get(`#${tabId}`).click(); }; -// FLAKY: https://github.com/elastic/kibana/issues/171644 -describe.skip('Artifact tabs in Policy Details page', { tags: ['@ess', '@serverless'] }, () => { +describe('Artifact tabs in Policy Details page', { tags: ['@ess', '@serverless'] }, () => { let endpointData: ReturnTypeFromChainable | undefined; before(() => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index 531b118f3fcd3..e7f32820c00d7 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -31,8 +31,7 @@ const loginWithoutAccess = (url: string) => { loadPage(url); }; -// FLAKY: https://github.com/elastic/kibana/issues/171168 -describe.skip('Artifacts pages', { tags: ['@ess', '@serverless'] }, () => { +describe('Artifacts pages', { tags: ['@ess', '@serverless'] }, () => { let endpointData: ReturnTypeFromChainable | undefined; before(() => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts index 00dc41b94ef4f..a736e05c33145 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts @@ -26,8 +26,7 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/169689 -describe.skip('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts index 4093581366321..b806323726018 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts @@ -22,8 +22,7 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/170674 -describe.skip('Document signing:', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +describe('Document signing:', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts index 3d02bc14251dd..75074b0d3f94a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts @@ -20,8 +20,7 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/169821 -describe.skip('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { beforeEach(() => { login(); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/reponse_actions_history.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts similarity index 92% rename from x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/reponse_actions_history.cy.ts rename to x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts index 0af8ee43f2988..a36f7e3beaec4 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/reponse_actions_history.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_actions_history.cy.ts @@ -10,10 +10,8 @@ import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; import { login } from '../../tasks/login'; import { loadPage } from '../../tasks/common'; -// FLAKY: https://github.com/elastic/kibana/issues/171641 -describe.skip('Response actions history page', { tags: ['@ess', '@serverless'] }, () => { +describe('Response actions history page', { tags: ['@ess', '@serverless'] }, () => { let endpointData: ReturnTypeFromChainable; - // let actionData: ReturnTypeFromChainable; before(() => { indexEndpointHosts({ numResponseActions: 11 }).then((indexEndpoints) => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolation.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts similarity index 51% rename from x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolation.cy.ts rename to x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts index 5b0d198742ede..44fdf9d63fb68 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolation.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts @@ -19,7 +19,6 @@ import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../../t import { checkEndpointListForOnlyIsolatedHosts, checkEndpointListForOnlyUnIsolatedHosts, - isolateHostFromEndpointList, } from '../../../tasks/isolate'; import { login } from '../../../tasks/login'; @@ -27,59 +26,44 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -// Failing: See https://github.com/elastic/kibana/issues/170470 -describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { - beforeEach(() => { - login(); - }); - - describe('Host Isolation:', () => { - let indexedPolicy: IndexedFleetEndpointPolicyResponse; - let policy: PolicyData; - let createdHost: CreateAndEnrollEndpointHostResponse; +describe('Response console', { tags: ['@ess', '@serverless'] }, () => { + let indexedPolicy: IndexedFleetEndpointPolicyResponse; + let policy: PolicyData; + let createdHost: CreateAndEnrollEndpointHostResponse; - before(() => { - getEndpointIntegrationVersion().then((version) => - createAgentPolicyTask(version).then((data) => { - indexedPolicy = data; - policy = indexedPolicy.integrationPolicies[0]; + before(() => { + getEndpointIntegrationVersion().then((version) => + createAgentPolicyTask(version).then((data) => { + indexedPolicy = data; + policy = indexedPolicy.integrationPolicies[0]; - return enableAllPolicyProtections(policy.id).then(() => { - // Create and enroll a new Endpoint host - return createEndpointHost(policy.policy_id).then((host) => { - createdHost = host as CreateAndEnrollEndpointHostResponse; - }); + return enableAllPolicyProtections(policy.id).then(() => { + // Create and enroll a new Endpoint host + return createEndpointHost(policy.policy_id).then((host) => { + createdHost = host as CreateAndEnrollEndpointHostResponse; }); - }) - ); - }); + }); + }) + ); + }); - after(() => { - if (createdHost) { - cy.task('destroyEndpointHost', createdHost); - } + after(() => { + if (createdHost) { + cy.task('destroyEndpointHost', createdHost); + } - if (indexedPolicy) { - cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy); - } + if (indexedPolicy) { + cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy); + } - if (createdHost) { - deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] }); - } - }); + if (createdHost) { + deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] }); + } + }); - it('should release an isolated host from response console', () => { - const command = 'release'; - waitForEndpointListPageToBeLoaded(createdHost.hostname); - // isolate the host first - isolateHostFromEndpointList(); - checkEndpointListForOnlyIsolatedHosts(); - openResponseConsoleFromEndpointList(); - performCommandInputChecks(command); - submitCommand(); - waitForCommandToBeExecuted(command); - waitForEndpointListPageToBeLoaded(createdHost.hostname); - checkEndpointListForOnlyUnIsolatedHosts(); + describe('Host Isolation:', () => { + beforeEach(() => { + login(); }); it('should isolate a host from response console', () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts new file mode 100644 index 0000000000000..8de8ca22ae8bf --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts @@ -0,0 +1,85 @@ +/* + * Copyright 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 { PolicyData } from '../../../../../../common/endpoint/types'; +import type { CreateAndEnrollEndpointHostResponse } from '../../../../../../scripts/endpoint/common/endpoint_host_services'; +import { + openResponseConsoleFromEndpointList, + performCommandInputChecks, + submitCommand, + waitForCommandToBeExecuted, + waitForEndpointListPageToBeLoaded, +} from '../../../tasks/response_console'; +import type { IndexedFleetEndpointPolicyResponse } from '../../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../../tasks/fleet'; +import { + checkEndpointListForOnlyIsolatedHosts, + checkEndpointListForOnlyUnIsolatedHosts, + isolateHostActionViaAPI, +} from '../../../tasks/isolate'; + +import { login } from '../../../tasks/login'; +import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; +import { createEndpointHost } from '../../../tasks/create_endpoint_host'; +import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; + +describe('Response console', { tags: ['@ess', '@serverless'] }, () => { + let indexedPolicy: IndexedFleetEndpointPolicyResponse; + let policy: PolicyData; + let createdHost: CreateAndEnrollEndpointHostResponse; + + before(() => { + getEndpointIntegrationVersion().then((version) => + createAgentPolicyTask(version).then((data) => { + indexedPolicy = data; + policy = indexedPolicy.integrationPolicies[0]; + + return enableAllPolicyProtections(policy.id).then(() => { + // Create and enroll a new Endpoint host + return createEndpointHost(policy.policy_id).then((host) => { + createdHost = host as CreateAndEnrollEndpointHostResponse; + }); + }); + }) + ); + }); + + after(() => { + if (createdHost) { + cy.task('destroyEndpointHost', createdHost); + } + + if (indexedPolicy) { + cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy); + } + + if (createdHost) { + deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] }); + } + }); + + describe('Host Isolation:', () => { + beforeEach(() => { + login(); + }); + + it('should release an isolated host via response console', () => { + const command = 'release'; + waitForEndpointListPageToBeLoaded(createdHost.hostname); + // isolate the host first + isolateHostActionViaAPI(createdHost.agentId); + // verify and find the isolated host + checkEndpointListForOnlyIsolatedHosts(); + openResponseConsoleFromEndpointList(); + performCommandInputChecks(command); + submitCommand(); + waitForCommandToBeExecuted(command); + waitForEndpointListPageToBeLoaded(createdHost.hostname); + checkEndpointListForOnlyUnIsolatedHosts(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/complete_with_endpoint_roles.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/complete_with_endpoint_roles.cy.ts index 722b31aca0391..a197574035b79 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/complete_with_endpoint_roles.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/complete_with_endpoint_roles.cy.ts @@ -30,8 +30,7 @@ import { openConsoleHelpPanel, } from '../../../screens'; -// FLAKY: https://github.com/elastic/kibana/issues/170052 -describe.skip( +describe( 'User Roles for Security Complete PLI with Endpoint Complete addon', { tags: ['@serverless'], diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts index 03ff413405b53..4e0ae2080a97b 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/roles/essentials_with_endpoint.roles.cy.ts @@ -23,8 +23,7 @@ import { ensurePolicyDetailsPageAuthzAccess, } from '../../../screens'; -// FLAKY: https://github.com/elastic/kibana/issues/170985 -describe.skip( +describe( 'Roles for Security Essential PLI with Endpoint Essentials addon', { tags: ['@serverless'], diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts index 34aba3fcfccf2..ed47855ac894a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts @@ -21,8 +21,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/170667 -describe.skip( +describe( 'Uninstall agent from host when agent tamper protection is disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts index 8f45e3d70b5e6..527566bed608b 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts @@ -22,8 +22,7 @@ import { login } from '../../../tasks/login'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/170601 -describe.skip( +describe( 'Uninstall agent from host when agent tamper protection is enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts index f5665d830eb4a..0768c4a49ca39 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts @@ -23,8 +23,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -// FLAKY: https://github.com/elastic/kibana/issues/170604 -describe.skip( +describe( 'Uninstall agent from host changing agent policy when agent tamper protection is enabled but then is switched to a policy with it disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts b/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts new file mode 100644 index 0000000000000..df3a5cf6d38a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kibanaPackageJson } from '@kbn/repo-info'; +import type { ToolingLog } from '@kbn/tooling-log'; +import type { KbnClient } from '@kbn/test/src/kbn_client'; +import { isFleetServerRunning } from '../../../../scripts/endpoint/common/fleet_server/fleet_server_services'; +import type { HostVm } from '../../../../scripts/endpoint/common/types'; +import type { BaseVmCreateOptions } from '../../../../scripts/endpoint/common/vm_services'; +import { createVm } from '../../../../scripts/endpoint/common/vm_services'; +import { + fetchAgentPolicyEnrollmentKey, + fetchFleetServerUrl, + getAgentDownloadUrl, + getAgentFileName, + getOrCreateDefaultAgentPolicy, + waitForHostToEnroll, +} from '../../../../scripts/endpoint/common/fleet_services'; +import type { DownloadedAgentInfo } from '../../../../scripts/endpoint/common/agent_downloads_service'; +import { + downloadAndStoreAgent, + isAgentDownloadFromDiskAvailable, +} from '../../../../scripts/endpoint/common/agent_downloads_service'; + +export interface CreateAndEnrollEndpointHostCIOptions + extends Pick { + kbnClient: KbnClient; + log: ToolingLog; + /** The fleet Agent Policy ID to use for enrolling the agent */ + agentPolicyId: string; + /** version of the Agent to install. Defaults to stack version */ + version?: string; + /** The name for the host. Will also be the name of the VM */ + hostname?: string; + /** If `version` should be exact, or if this is `true`, then the closest version will be used. Defaults to `false` */ + useClosestVersionMatch?: boolean; +} + +export interface CreateAndEnrollEndpointHostCIResponse { + hostname: string; + agentId: string; + hostVm: HostVm; +} + +/** + * Creates a new virtual machine (host) and enrolls that with Fleet + */ +export const createAndEnrollEndpointHostCI = async ({ + kbnClient, + log, + agentPolicyId, + cpus, + disk, + memory, + hostname, + version = kibanaPackageJson.version, + useClosestVersionMatch = true, +}: CreateAndEnrollEndpointHostCIOptions): Promise => { + const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`; + + const fileNameNoExtension = getAgentFileName(version); + const agentFileName = `${fileNameNoExtension}.tar.gz`; + let agentDownload: DownloadedAgentInfo | undefined; + + // Check if agent file is already on disk before downloading it again + agentDownload = isAgentDownloadFromDiskAvailable(agentFileName); + + // If it has not been already downloaded, it should be downloaded. + if (!agentDownload) { + log.warning( + `There is no agent installer for ${agentFileName} present on disk, trying to download it now.` + ); + const { url: agentUrl } = await getAgentDownloadUrl(version, useClosestVersionMatch, log); + agentDownload = await downloadAndStoreAgent(agentUrl, agentFileName); + } + + const hostVm = await createVm({ + type: 'vagrant', + name: vmName, + log, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agentDownload: agentDownload!, + disk, + cpus, + memory, + }); + + if (!(await isFleetServerRunning(kbnClient))) { + throw new Error(`Fleet server does not seem to be running on this instance of kibana!`); + } + + const policyId = agentPolicyId || (await getOrCreateDefaultAgentPolicy({ kbnClient, log })).id; + const [fleetServerUrl, enrollmentToken] = await Promise.all([ + fetchFleetServerUrl(kbnClient), + fetchAgentPolicyEnrollmentKey(kbnClient, policyId), + ]); + + const agentEnrollCommand = [ + 'sudo', + + `./${fileNameNoExtension}/elastic-agent`, + + 'install', + + '--insecure', + + '--force', + + '--url', + fleetServerUrl, + + '--enrollment-token', + enrollmentToken, + ].join(' '); + + log.info(`Enrolling Elastic Agent with Fleet`); + log.verbose('Enrollment command:', agentEnrollCommand); + + await hostVm.exec(agentEnrollCommand); + + const { id: agentId } = await waitForHostToEnroll(kbnClient, log, hostVm.name, 240000); + + return { + hostname: hostVm.name, + agentId, + hostVm, + }; +}; 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 index 99ea877053c91..299d210a65089 100644 --- 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 @@ -26,10 +26,7 @@ import { import type { DeleteAllEndpointDataResponse } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; import { deleteAllEndpointData } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; import { waitForEndpointToStreamData } from '../../../../scripts/endpoint/common/endpoint_metadata_services'; -import type { - CreateAndEnrollEndpointHostOptions, - CreateAndEnrollEndpointHostResponse, -} from '../../../../scripts/endpoint/common/endpoint_host_services'; +import type { CreateAndEnrollEndpointHostResponse } from '../../../../scripts/endpoint/common/endpoint_host_services'; import { createAndEnrollEndpointHost, destroyEndpointHost, @@ -66,6 +63,11 @@ import { indexFleetEndpointPolicy, } from '../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; import { cyLoadEndpointDataHandler } from './plugin_handlers/endpoint_data_loader'; +import type { + CreateAndEnrollEndpointHostCIOptions, + CreateAndEnrollEndpointHostCIResponse, +} from './create_and_enroll_endpoint_host_ci'; +import { createAndEnrollEndpointHostCI } from './create_and_enroll_endpoint_host_ci'; /** * Test Role/User loader for cypress. Checks to see if running in serverless and handles it as appropriate @@ -290,40 +292,48 @@ export const dataLoadersForRealEndpoints = ( on('task', { createEndpointHost: async ( - options: Omit - ): Promise => { + options: Omit + ): Promise => { const { kbnClient, log } = await stackServicesPromise; let retryAttempt = 0; - const attemptCreateEndpointHost = async (): Promise => { - try { - log.info(`Creating endpoint host, attempt ${retryAttempt}`); - const newHost = await createAndEnrollEndpointHost({ - useClosestVersionMatch: true, - ...options, - log, - kbnClient, - }); - await waitForEndpointToStreamData(kbnClient, newHost.agentId, 360000); - return newHost; - } catch (err) { - log.info(`Caught error when setting up the agent: ${err}`); - if (retryAttempt === 0 && err.agentId) { - retryAttempt++; - await destroyEndpointHost(kbnClient, { - hostname: err.hostname || '', // No hostname in CI env for vagrant - agentId: err.agentId, - }); - log.info(`Deleted endpoint host ${err.agentId} and retrying`); - return attemptCreateEndpointHost(); - } else { - log.info( - `${retryAttempt} attempts of creating endpoint host failed, reason for the last failure was ${err}` - ); - throw err; + const attemptCreateEndpointHost = + async (): Promise => { + try { + log.info(`Creating endpoint host, attempt ${retryAttempt}`); + const newHost = process.env.CI + ? await createAndEnrollEndpointHostCI({ + useClosestVersionMatch: true, + ...options, + log, + kbnClient, + }) + : await createAndEnrollEndpointHost({ + useClosestVersionMatch: true, + ...options, + log, + kbnClient, + }); + await waitForEndpointToStreamData(kbnClient, newHost.agentId, 360000); + return newHost; + } catch (err) { + log.info(`Caught error when setting up the agent: ${err}`); + if (retryAttempt === 0 && err.agentId) { + retryAttempt++; + await destroyEndpointHost(kbnClient, { + hostname: err.hostname || '', // No hostname in CI env for vagrant + agentId: err.agentId, + }); + log.info(`Deleted endpoint host ${err.agentId} and retrying`); + return attemptCreateEndpointHost(); + } else { + log.info( + `${retryAttempt} attempts of creating endpoint host failed, reason for the last failure was ${err}` + ); + throw err; + } } - } - }; + }; return attemptCreateEndpointHost(); }, diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts index 10aec51af291d..20d57ba7a94b5 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts @@ -7,9 +7,11 @@ /* eslint-disable cypress/no-unnecessary-waiting */ +import { API_VERSIONS } from '@kbn/fleet-plugin/common'; import { openAlertDetailsView } from '../screens/alerts'; import type { ActionDetails } from '../../../../common/endpoint/types'; import { loadPage } from './common'; +import { waitForActionToSucceed } from './response_actions'; const API_ENDPOINT_ACTION_PATH = '/api/endpoint/action/*'; export const interceptActionRequests = ( @@ -41,29 +43,6 @@ export const isolateHostWithComment = (comment: string, hostname: string): void cy.getByTestSubj('host_isolation_comment').type(comment); }; -export const isolateHostFromEndpointList = (index: number = 0): void => { - // open the action menu and click isolate action - cy.getByTestSubj('endpointTableRowActions').eq(index).click(); - cy.getByTestSubj('isolateLink').click(); - // isolation form, click confirm button - cy.getByTestSubj('hostIsolateConfirmButton').click(); - // return to endpoint details - cy.getByTestSubj('hostIsolateSuccessCompleteButton').click(); - // close details flyout - cy.getByTestSubj('euiFlyoutCloseButton').click(); - - // ensure the host is isolated, wait for 3 minutes for the host to be isolated - cy.wait(18000); - - cy.getByTestSubj('endpointListTable').within(() => { - cy.get('tbody tr') - .eq(index) - .within(() => { - cy.get('td').eq(1).should('contain.text', 'Isolated'); - }); - }); -}; - export const releaseHostWithComment = (comment: string, hostname: string): void => { cy.contains(`${hostname} is currently isolated.`); cy.getByTestSubj('endpointHostIsolationForm'); @@ -139,28 +118,46 @@ export const filterOutIsolatedHosts = (): void => { cy.getByTestSubj('querySubmitButton').click(); }; -const checkEndpointListForIsolatedHosts = (expectIsolated: boolean): void => { - const chainer = expectIsolated ? 'contain.text' : 'not.contain.text'; +const checkEndpointListForIsolationStatus = (expectIsolated: boolean): void => { + const chainer = expectIsolated ? 'contain' : 'not.contain'; cy.getByTestSubj('endpointListTable').within(() => { - cy.get('tbody tr').each(($tr) => { - cy.wrap($tr).within(() => { + cy.get('tbody tr') + .eq(0) + .within(() => { cy.get('td').eq(1).should(chainer, 'Isolated'); }); - }); }); }; export const checkEndpointListForOnlyUnIsolatedHosts = (): void => - checkEndpointListForIsolatedHosts(false); + checkEndpointListForIsolationStatus(false); export const checkEndpointListForOnlyIsolatedHosts = (): void => - checkEndpointListForIsolatedHosts(true); + checkEndpointListForIsolationStatus(true); + +export const isolateHostActionViaAPI = (agentId: string): void => { + cy.request({ + headers: { + 'kbn-xsrf': 'cypress-creds', + 'elastic-api-version': API_VERSIONS.public.v1, + }, + method: 'POST', + url: 'api/endpoint/action/isolate', + body: { + endpoint_ids: [agentId], + }, + }) + // verify action was successful + .then((response) => waitForActionToSucceed(response.body.data.id)) + .then((actionResponse) => { + expect(actionResponse.status).to.equal('successful'); + }); +}; export const checkEndpointIsolationStatus = ( endpointHostname: string, expectIsolated: boolean ): void => { - const chainer = expectIsolated ? 'contain.text' : 'not.contain.text'; - + const chainer = expectIsolated ? 'contain' : 'not.contain'; cy.contains(endpointHostname).parents('td').siblings('td').eq(0).should(chainer, 'Isolated'); }; diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts index 387f86c0dd160..126d637f07edb 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_actions.ts @@ -139,6 +139,38 @@ export const waitForActionToComplete = ( }); }; +export const waitForActionToSucceed = ( + actionId: string, + timeout = 180000 +): Cypress.Chainable => { + let action: ActionDetails | undefined; + + return cy + .waitUntil( + () => { + return request({ + method: 'GET', + url: resolvePathVariables(ACTION_DETAILS_ROUTE, { action_id: actionId || 'undefined' }), + }).then((response) => { + if (response.body.data.isCompleted && response.body.data.status === 'successful') { + action = response.body.data; + return true; + } + + return false; + }); + }, + { timeout, interval: 2000 } + ) + .then(() => { + if (!action) { + throw new Error('Failed to retrieve successful action'); + } + + return action; + }); +}; + /** * Ensure user has the given `accessLevel` to the type of response action * @param accessLevel diff --git a/x-pack/plugins/security_solution/public/mocks.ts b/x-pack/plugins/security_solution/public/mocks.ts index 044e58192f2d3..6564697c95f08 100644 --- a/x-pack/plugins/security_solution/public/mocks.ts +++ b/x-pack/plugins/security_solution/public/mocks.ts @@ -16,7 +16,7 @@ const upselling = new UpsellingService(); export const contractStartServicesMock: ContractStartServices = { extraRoutes$: of([]), - getComponent$: jest.fn(), + getComponents$: jest.fn(() => of({})), upselling, }; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts index f2e3296133ffd..22fe8a3d52823 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts @@ -25,7 +25,7 @@ export const VIEW_ALL = i18n.translate( } ); -export const LEARN_MORE = (riskEntity: RiskScoreEntity) => +export const LEARN_MORE = (riskEntity?: RiskScoreEntity) => i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.learnMore', { defaultMessage: 'Learn more about {riskEntity} risk', values: { diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx index 5e75ed3643a71..134fac9ac7295 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx @@ -7,14 +7,14 @@ import React from 'react'; -import { useGetUserCasesPermissions, useKibana } from '../../../common/lib/kibana'; +import { useKibana } from '../../../common/lib/kibana'; import { APP_ID } from '../../../../common/constants'; const MAX_CASES_TO_SHOW = 3; const RecentCasesComponent = () => { const { cases } = useKibana().services; - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); return cases.ui.getRecentCases({ permissions: userCasesPermissions, diff --git a/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.test.tsx b/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.test.tsx index 692ffd0b44ec8..8835fc0e48f27 100644 --- a/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.test.tsx @@ -10,7 +10,7 @@ import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; import { TestProviders } from '../../../common/mock'; import { Sidebar } from './sidebar'; -import { useGetUserCasesPermissions, useKibana } from '../../../common/lib/kibana'; +import { useKibana } from '../../../common/lib/kibana'; import type { CaseUiClientMock } from '@kbn/cases-plugin/public/mocks'; import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; import { noCasesPermissions, readCasesPermissions } from '../../../cases_test_utils'; @@ -38,7 +38,7 @@ describe('Sidebar', () => { }); it('does not render the recently created cases section when the user does not have read permissions', async () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(noCasesPermissions()); + casesMock.helpers.canUseCases.mockReturnValue(noCasesPermissions()); await waitFor(() => mount( @@ -52,7 +52,7 @@ describe('Sidebar', () => { }); it('does render the recently created cases section when the user has read permissions', async () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(readCasesPermissions()); + casesMock.helpers.canUseCases.mockReturnValue(readCasesPermissions()); await waitFor(() => mount( diff --git a/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.tsx b/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.tsx index 4f87ec1d86605..501e03a65cb02 100644 --- a/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/sidebar/sidebar.tsx @@ -8,7 +8,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { ENABLE_NEWS_FEED_SETTING, NEWS_FEED_URL_SETTING } from '../../../../common/constants'; +import { useKibana } from '../../../common/lib/kibana/kibana_react'; +import { + APP_ID, + ENABLE_NEWS_FEED_SETTING, + NEWS_FEED_URL_SETTING, +} from '../../../../common/constants'; import { Filters as RecentTimelinesFilters } from '../recent_timelines/filters'; import { StatefulRecentTimelines } from '../recent_timelines'; import { StatefulNewsFeed } from '../../../common/components/news_feed'; @@ -17,7 +22,6 @@ import { SidebarHeader } from '../../../common/components/sidebar_header'; import * as i18n from '../../pages/translations'; import { RecentCases } from '../recent_cases'; -import { useGetUserCasesPermissions } from '../../../common/lib/kibana'; const SidebarSpacerComponent = () => ( @@ -30,6 +34,7 @@ export const Sidebar = React.memo<{ recentTimelinesFilterBy: RecentTimelinesFilterMode; setRecentTimelinesFilterBy: (filterBy: RecentTimelinesFilterMode) => void; }>(({ recentTimelinesFilterBy, setRecentTimelinesFilterBy }) => { + const { cases } = useKibana().services; const recentTimelinesFilters = useMemo( () => ( diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.test.tsx index 5fbe5d80f99ce..3e075d27ac76b 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.test.tsx @@ -30,14 +30,6 @@ jest.mock('../../common/lib/kibana', () => { return { ...original, KibanaServices: mockKibanaServices, - useGetUserCasesPermissions: () => ({ - all: false, - create: false, - read: true, - update: false, - delete: false, - push: false, - }), useKibana: jest.fn(), useUiSetting$: () => ['0,0.[000]'], }; @@ -80,6 +72,16 @@ describe('DataQuality', () => { hooks: { useCasesAddToNewCaseFlyout: jest.fn(), }, + helpers: { + canUseCases: jest.fn().mockReturnValue({ + all: false, + create: false, + read: true, + update: false, + delete: false, + push: false, + }), + }, }, configSettings: { ILMEnabled: true }, }, @@ -307,6 +309,16 @@ describe('DataQuality', () => { hooks: { useCasesAddToNewCaseFlyout: jest.fn(), }, + helpers: { + canUseCases: jest.fn().mockReturnValue({ + all: false, + create: false, + read: true, + update: false, + delete: false, + push: false, + }), + }, }, configSettings: { ILMEnabled: false }, }, diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx index 0ef421977a402..0c318b38a4660 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx @@ -38,15 +38,9 @@ import { HeaderPage } from '../../common/components/header_page'; import { LandingPageComponent } from '../../common/components/landing_page'; import { useLocalStorage } from '../../common/components/local_storage'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; -import { DEFAULT_BYTES_FORMAT, DEFAULT_NUMBER_FORMAT } from '../../../common/constants'; +import { APP_ID, DEFAULT_BYTES_FORMAT, DEFAULT_NUMBER_FORMAT } from '../../../common/constants'; import { useSourcererDataView } from '../../common/containers/sourcerer'; -import { - KibanaServices, - useGetUserCasesPermissions, - useKibana, - useToasts, - useUiSetting$, -} from '../../common/lib/kibana'; +import { KibanaServices, useKibana, useToasts, useUiSetting$ } from '../../common/lib/kibana'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; import * as i18n from './translations'; @@ -141,9 +135,7 @@ const DataQualityComponent: React.FC = () => { const httpFetch = KibanaServices.get().http.fetch; const { baseTheme, theme } = useThemes(); const toasts = useToasts(); - const { - services: { telemetry }, - } = useKibana(); + const addSuccessToast = useCallback( (toast: { title: string }) => { toasts.addSuccess(toast); @@ -156,7 +148,7 @@ const DataQualityComponent: React.FC = () => { const [selectedOptions, setSelectedOptions] = useState(defaultOptions); const { indicesExist, loading: isSourcererLoading, selectedPatterns } = useSourcererDataView(); const { signalIndexName, loading: isSignalIndexNameLoading } = useSignalIndex(); - const { configSettings, cases } = useKibana().services; + const { configSettings, cases, telemetry } = useKibana().services; const isILMAvailable = configSettings.ILMEnabled; const [startDate, setStartTime] = useState(); @@ -210,7 +202,7 @@ const DataQualityComponent: React.FC = () => { key: LOCAL_STORAGE_KEY, }); - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const canUserCreateAndReadCases = useCallback( () => userCasesPermissions.create && userCasesPermissions.read, [userCasesPermissions.create, userCasesPermissions.read] diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx index 60ee2d02e6b25..2b5561f58cb5c 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.test.tsx @@ -11,6 +11,7 @@ import { render } from '@testing-library/react'; import { DetectionResponse } from './detection_response'; import { TestProviders } from '../../common/mock'; import { noCasesPermissions, readCasesPermissions } from '../../cases_test_utils'; +import { useKibana as mockUseKibana } from '../../common/lib/kibana/__mocks__'; jest.mock('../components/detection_response/alerts_by_status', () => ({ AlertsByStatus: () =>
    , @@ -75,12 +76,24 @@ jest.mock('../../detections/containers/detection_engine/alerts/use_alerts_privil })); const defaultUseCasesPermissionsReturn = readCasesPermissions(); -const mockUseCasesPermissions = jest.fn(() => defaultUseCasesPermissionsReturn); -jest.mock('../../common/lib/kibana/hooks', () => { - const original = jest.requireActual('../../common/lib/kibana/hooks'); + +const mockedUseKibana = mockUseKibana(); +const mockCanUseCases = jest.fn(); + +jest.mock('../../common/lib/kibana', () => { + const original = jest.requireActual('../../common/lib/kibana'); + return { ...original, - useGetUserCasesPermissions: () => mockUseCasesPermissions(), + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + cases: { + helpers: { canUseCases: mockCanUseCases }, + }, + }, + }), }; }); @@ -90,7 +103,7 @@ describe('DetectionResponse', () => { mockUseSourcererDataView.mockReturnValue(defaultUseSourcererReturn); mockUseAlertsPrivileges.mockReturnValue(defaultUseAlertsPrivilegesReturn); mockUseSignalIndex.mockReturnValue(defaultUseSignalIndexReturn); - mockUseCasesPermissions.mockReturnValue(defaultUseCasesPermissionsReturn); + mockCanUseCases.mockReturnValue(defaultUseCasesPermissionsReturn); }); it('should render default page', () => { @@ -197,7 +210,7 @@ describe('DetectionResponse', () => { }); it('should not render cases data sections if the user does not have cases read permission', () => { - mockUseCasesPermissions.mockReturnValue(noCasesPermissions()); + mockCanUseCases.mockReturnValue(noCasesPermissions()); const result = render( @@ -218,7 +231,7 @@ describe('DetectionResponse', () => { }); it('should render page permissions message if the user does not have read permission', () => { - mockUseCasesPermissions.mockReturnValue(noCasesPermissions()); + mockCanUseCases.mockReturnValue(noCasesPermissions()); mockUseAlertsPrivileges.mockReturnValue({ hasKibanaREAD: true, hasIndexRead: false, diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 8bdc95fc69aab..77bbdbe8816ed 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import type { DocLinks } from '@kbn/doc-links'; +import { APP_ID } from '../../../common'; import { InputsModelId } from '../../common/store/inputs/constants'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { SocTrends } from '../components/detection_response/soc_trends'; @@ -18,7 +19,6 @@ import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; import { useAlertsPrivileges } from '../../detections/containers/detection_engine/alerts/use_alerts_privileges'; import { HeaderPage } from '../../common/components/header_page'; -import { useGetUserCasesPermissions } from '../../common/lib/kibana'; import { LandingPageComponent } from '../../common/components/landing_page'; import { AlertsByStatus } from '../components/detection_response/alerts_by_status'; @@ -31,13 +31,16 @@ import { CasesByStatus } from '../components/detection_response/cases_by_status' import { NoPrivileges } from '../../common/components/no_privileges'; import { FiltersGlobal } from '../../common/components/filters_global'; import { useGlobalFilterQuery } from '../../common/hooks/use_global_filter_query'; +import { useKibana } from '../../common/lib/kibana'; const DetectionResponseComponent = () => { + const { cases } = useKibana().services; const { filterQuery } = useGlobalFilterQuery(); const { indicesExist, loading: isSourcererLoading, sourcererDataView } = useSourcererDataView(); const { signalIndexName } = useSignalIndex(); const { hasKibanaREAD, hasIndexRead } = useAlertsPrivileges(); - const canReadCases = useGetUserCasesPermissions().read; + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); + const canReadCases = userCasesPermissions.read; const canReadAlerts = hasKibanaREAD && hasIndexRead; const isSocTrendsEnabled = useIsExperimentalFeatureEnabled('socTrendsEnabled'); if (!canReadAlerts && !canReadCases) { diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index c07273e59ed4a..e36dcd7991720 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -235,11 +235,6 @@ export class Plugin implements IPlugin
    - .c2 { - display: block; + .c3:active, +.c3:focus { + background: transparent; } -.c1 > span { +.c3 > span { padding: 0; } -.c3 { +.c4 { overflow: hidden; display: inline-block; text-overflow: ellipsis; + white-space: nowrap; } -.c0 { +.c5 { + white-space: nowrap; +} + +.c1 { + overflow-x: auto; +} + +.c2 { overflow: hidden; } +.c0 { + backgroundColor: #1d1e24; + color: #dfe5ef; + padding-inline: 12px; + border-radius: 0px; +} +
    - -
    -
    -
    -
    - + + +
    +
    +
    +
    + + Unsaved + +
    +
    +
    + +
    +

    diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.test.tsx new file mode 100644 index 0000000000000..7203e74fe02c1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.test.tsx @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import { TestProviders, mockIndexNames, mockIndexPattern } from '../../../../common/mock'; +import { useSourcererDataView } from '../../../../common/containers/sourcerer'; +import { allCasesPermissions, readCasesPermissions } from '../../../../cases_test_utils'; +import { mockBrowserFields } from '../../../../common/containers/source/mock'; +import { TimelineActionMenu } from '.'; +import { TimelineId, TimelineTabs } from '../../../../../common/types'; +import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; + +const mockUseSourcererDataView: jest.Mock = useSourcererDataView as jest.Mock; +const mockedUseKibana = mockUseKibana(); +const mockCanUseCases = jest.fn(); + +jest.mock('../../../../common/containers/sourcerer'); + +jest.mock('../../../../common/lib/kibana/kibana_react', () => { + const original = jest.requireActual('../../../../common/lib/kibana/kibana_react'); + + return { + ...original, + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + cases: { + ...mockedUseKibana.services.cases, + helpers: { canUseCases: mockCanUseCases }, + }, + }, + application: { + capabilities: { + navLinks: {}, + management: {}, + catalogue: {}, + actions: { show: true, crud: true }, + }, + }, + }), + }; +}); + +jest.mock('@kbn/i18n-react', () => { + const originalModule = jest.requireActual('@kbn/i18n-react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); + +const sourcererDefaultValue = { + sourcererDefaultValue: mockBrowserFields, + indexPattern: mockIndexPattern, + loading: false, + selectedPatterns: mockIndexNames, +}; + +describe('Action menu', () => { + beforeEach(() => { + // Mocking these services is required for the header component to render. + mockUseSourcererDataView.mockImplementation(() => sourcererDefaultValue); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('AddToCaseButton', () => { + it('renders the button when the user has create and read permissions', () => { + mockCanUseCases.mockReturnValue(allCasesPermissions()); + + render( + + + + ); + + expect(screen.getByTestId('attach-timeline-case-button')).toBeInTheDocument(); + }); + + it('does not render the button when the user does not have create permissions', () => { + mockCanUseCases.mockReturnValue(readCasesPermissions()); + + render( + + + + ); + + expect(screen.queryByTestId('attach-timeline-case-button')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx new file mode 100644 index 0000000000000..04f7b2eb0a31e --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; +import { APP_ID } from '../../../../../common'; +import type { TimelineTabs } from '../../../../../common/types'; +import { InspectButton } from '../../../../common/components/inspect'; +import { InputsModelId } from '../../../../common/store/inputs/constants'; +import { AddToCaseButton } from '../add_to_case_button'; +import { NewTimelineAction } from './new_timeline'; +import { SaveTimelineButton } from './save_timeline_button'; +import { OpenTimelineAction } from './open_timeline'; + +interface TimelineActionMenuProps { + mode?: 'compact' | 'normal'; + timelineId: string; + isInspectButtonDisabled: boolean; + activeTab: TimelineTabs; +} + +const TimelineActionMenuComponent = ({ + mode = 'normal', + timelineId, + activeTab, + isInspectButtonDisabled, +}: TimelineActionMenuProps) => { + const { cases } = useKibana().services; + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); + + return ( + + + + + + + + {userCasesPermissions.create && userCasesPermissions.read ? ( + + + + ) : null} + + + + + + + + ); +}; + +export const TimelineActionMenu = React.memo(TimelineActionMenuComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/new_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/new_timeline.tsx new file mode 100644 index 0000000000000..8fc65250c5b4a --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/new_timeline.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 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 { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui'; +import React, { useMemo, useState, useCallback } from 'react'; +import { NewTimeline } from '../../timeline/properties/helpers'; +import { NewTemplateTimeline } from '../../timeline/properties/new_template_timeline'; +import * as i18n from './translations'; + +interface NewTimelineActionProps { + timelineId: string; +} + +const panelStyle = { + padding: 0, +}; + +export const NewTimelineAction = React.memo(({ timelineId }: NewTimelineActionProps) => { + const [isPopoverOpen, setPopover] = useState(false); + + const closePopover = useCallback(() => { + setPopover(false); + }, []); + + const togglePopover = useCallback(() => setPopover((prev) => !prev), []); + + const newTimelineActionbtn = useMemo(() => { + return ( + + {i18n.NEW_TIMELINE_BTN} + + ); + }, [togglePopover]); + + return ( + + + + + + + + + + + ); +}); + +NewTimelineAction.displayName = 'NewTimelineAction'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/open_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/open_timeline.tsx new file mode 100644 index 0000000000000..f5a7a51dc75e8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/open_timeline.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButtonEmpty } from '@elastic/eui'; +import React, { useState, useCallback } from 'react'; +import { OpenTimelineModal } from '../../open_timeline/open_timeline_modal'; +import type { ActionTimelineToShow } from '../../open_timeline/types'; +import * as i18n from './translations'; + +const actionTimelineToHide: ActionTimelineToShow[] = ['createFrom']; + +export const OpenTimelineAction = React.memo(() => { + const [showTimelineModal, setShowTimelineModal] = useState(false); + const onCloseTimelineModal = useCallback(() => setShowTimelineModal(false), []); + const onOpenTimelineModal = useCallback(() => { + setShowTimelineModal(true); + }, []); + + return ( + <> + + {i18n.OPEN_TIMELINE_BTN} + + + {showTimelineModal ? ( + + ) : null} + + ); +}); + +OpenTimelineAction.displayName = 'OpenTimelineAction'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.test.tsx similarity index 83% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.test.tsx rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.test.tsx index 7c05526594501..92bc0be3f54e1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.test.tsx @@ -11,7 +11,7 @@ import type { SaveTimelineButtonProps } from './save_timeline_button'; import { SaveTimelineButton } from './save_timeline_button'; import { TestProviders } from '../../../../common/mock'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; -import { getTimelineStatusByIdSelector } from '../../flyout/header/selectors'; +import { getTimelineStatusByIdSelector } from '../header/selectors'; import { TimelineStatus } from '../../../../../common/api/timeline'; const TEST_ID = { @@ -28,7 +28,7 @@ jest.mock('react-redux', () => { jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/components/user_privileges'); -jest.mock('../../flyout/header/selectors', () => { +jest.mock('../header/selectors', () => { return { getTimelineStatusByIdSelector: jest.fn().mockReturnValue(() => ({ status: 'draft', @@ -64,7 +64,28 @@ describe('SaveTimelineButton', () => { expect(screen.getByRole('button')).toBeDisabled(); }); + it('should disable the save timeline button when the timeline is immutable', () => { + (useUserPrivileges as jest.Mock).mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true }, + }); + (getTimelineStatusByIdSelector as jest.Mock).mockReturnValue(() => ({ + status: TimelineStatus.immutable, + })); + render( + + + + ); + expect(screen.getByRole('button')).toBeDisabled(); + }); + describe('with draft timeline', () => { + beforeAll(() => { + (getTimelineStatusByIdSelector as jest.Mock).mockReturnValue(() => ({ + status: TimelineStatus.draft, + })); + }); + it('should not show the save modal if user does not have write access', async () => { (useUserPrivileges as jest.Mock).mockReturnValue({ kibanaSecuritySolutionsPrivileges: { crud: false }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.tsx similarity index 86% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.tsx rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.tsx index 7a025259a6574..5f52f67185b5c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_button.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_button.tsx @@ -8,27 +8,24 @@ import React, { useCallback, useMemo, useState } from 'react'; import { EuiButton, EuiToolTip, EuiTourStep, EuiCode, EuiText, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { getTimelineStatusByIdSelector } from '../../flyout/header/selectors'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { TimelineStatus } from '../../../../../common/api/timeline'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { useIsElementMounted } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted'; import { useLocalStorage } from '../../../../common/components/local_storage'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { SaveTimelineModal } from './save_timeline_modal'; import * as timelineTranslations from './translations'; +import { getTimelineStatusByIdSelector } from '../header/selectors'; export interface SaveTimelineButtonProps { timelineId: string; } const SAVE_BUTTON_ELEMENT_ID = 'SAVE_BUTTON_ELEMENT_ID'; -const LOCAL_STORAGE_KEY = 'security.timelineFlyoutHeader.saveTimelineTourSeen'; +const LOCAL_STORAGE_KEY = 'security.timelineFlyoutHeader.saveTimelineTour'; export const SaveTimelineButton = React.memo(({ timelineId }) => { - const isTimelineSaveTourSaveTourDisabled = - useIsExperimentalFeatureEnabled('disableTimelineSaveTour'); const [showEditTimelineOverlay, setShowEditTimelineOverlay] = useState(false); const closeSaveTimeline = useCallback(() => { @@ -45,7 +42,7 @@ export const SaveTimelineButton = React.memo(({ timelin // TODO: User may have Crud privileges but they may not have access to timeline index. // Do we need to check that? const { - kibanaSecuritySolutionsPrivileges: { crud: canEditTimeline }, + kibanaSecuritySolutionsPrivileges: { crud: canEditTimelinePrivilege }, } = useUserPrivileges(); const getTimelineStatus = useMemo(() => getTimelineStatusByIdSelector(), []); const { @@ -56,27 +53,30 @@ export const SaveTimelineButton = React.memo(({ timelin } = useDeepEqualSelector((state) => getTimelineStatus(state, timelineId)); const isSaveButtonMounted = useIsElementMounted(SAVE_BUTTON_ELEMENT_ID); - const [hasSeenTimelineSaveTour, setHasSeenTimelineSaveTour] = useLocalStorage({ - defaultValue: false, + const [timelineTourStatus, setTimelineTourStatus] = useLocalStorage({ + defaultValue: { isTourActive: true }, key: LOCAL_STORAGE_KEY, + isInvalidDefault: (valueFromStorage) => { + return !valueFromStorage; + }, }); + + const canEditTimeline = canEditTimelinePrivilege && timelineStatus !== TimelineStatus.immutable; // Why are we checking for so many flags here? // The tour popup should only show when timeline is fully populated and all necessary // elements are visible on screen. If we would not check for all these flags, the tour // popup would show too early and in the wrong place in the DOM. // The last flag, checks if the tour has been dismissed before. const showTimelineSaveTour = - // The timeline save tour could be disabled on a plugin level - !isTimelineSaveTourSaveTourDisabled && canEditTimeline && isVisible && !isLoading && isSaveButtonMounted && - !hasSeenTimelineSaveTour; + timelineTourStatus?.isTourActive; const markTimelineSaveTourAsSeen = useCallback(() => { - setHasSeenTimelineSaveTour(true); - }, [setHasSeenTimelineSaveTour]); + setTimelineTourStatus({ isTourActive: false }); + }, [setTimelineTourStatus]); const isUnsaved = timelineStatus === TimelineStatus.draft; const tooltipContent = canEditTimeline ? null : timelineTranslations.CALL_OUT_UNAUTHORIZED_MSG; @@ -92,10 +92,11 @@ export const SaveTimelineButton = React.memo(({ timelin fill color="primary" onClick={openEditTimeline} + size="s" iconType="save" isLoading={isSaving} disabled={!canEditTimeline} - data-test-subj="save-timeline-btn" + data-test-subj="save-timeline-action-btn" id={SAVE_BUTTON_ELEMENT_ID} > {timelineTranslations.SAVE} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.test.tsx similarity index 93% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.test.tsx rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.test.tsx index 8ee6bf807913a..43cdb85da8c30 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.test.tsx @@ -18,7 +18,7 @@ jest.mock('../../../../common/hooks/use_selector', () => ({ useDeepEqualSelector: jest.fn(), })); -jest.mock('../properties/use_create_timeline', () => ({ +jest.mock('../../timeline/properties/use_create_timeline', () => ({ useCreateTimeline: jest.fn(), })); @@ -114,6 +114,13 @@ describe('EditTimelineModal', () => { }); expect(component.find('[data-test-subj="save-button"]').exists()).toEqual(true); }); + + test('Does not show save as new switch', () => { + const component = mount(, { + wrappingComponent: TestProviders, + }); + expect(component.find('[data-test-subj="save-as-new-switch"]').exists()).toEqual(false); + }); }); describe('update timeline', () => { @@ -192,6 +199,13 @@ describe('EditTimelineModal', () => { }); expect(component.find('[data-test-subj="save-button"]').exists()).toEqual(true); }); + + test('Show save as new switch', () => { + const component = mount(, { + wrappingComponent: TestProviders, + }); + expect(component.find('[data-test-subj="save-as-new-switch"]').exists()).toEqual(true); + }); }); describe('showWarning', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx similarity index 77% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.tsx rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx index f38cb4784ec17..10154901f2db7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/save_timeline_modal.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx @@ -16,8 +16,10 @@ import { EuiSpacer, EuiProgress, EuiCallOut, + EuiSwitch, } from '@elastic/eui'; -import React, { useCallback, useEffect, useMemo } from 'react'; +import type { EuiSwitchEvent } from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import usePrevious from 'react-use/lib/usePrevious'; @@ -26,13 +28,13 @@ import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; -import { NOTES_PANEL_WIDTH } from '../properties/notes_size'; -import { useCreateTimeline } from '../properties/use_create_timeline'; -import * as commonI18n from '../properties/translations'; +import * as commonI18n from '../../timeline/properties/translations'; import * as i18n from './translations'; -import { formSchema } from './schema'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; import { TIMELINE_ACTIONS } from '../../../../common/lib/apm/user_actions'; +import { useCreateTimeline } from '../../timeline/properties/use_create_timeline'; +import { NOTES_PANEL_WIDTH } from '../../timeline/properties/notes_size'; +import { formSchema } from './schema'; const CommonUseField = getUseField({ component: Field }); interface SaveTimelineModalProps { @@ -62,6 +64,7 @@ export const SaveTimelineModal = React.memo( getTimeline(state, timelineId) ) ); + const isUnsaved = status === TimelineStatus.draft; const prevIsSaving = usePrevious(isSaving); const dispatch = useDispatch(); // Resetting the timeline by replacing the active one with a new empty one @@ -69,6 +72,12 @@ export const SaveTimelineModal = React.memo( timelineId: TimelineId.active, timelineType: TimelineType.default, }); + const [saveAsNewTimeline, setSaveAsNewTimeline] = useState(false); + + const onSaveAsNewChanged = useCallback( + (e: EuiSwitchEvent) => setSaveAsNewTimeline(e.target.checked), + [] + ); const handleSubmit = useCallback( (titleAndDescription, isValid) => { @@ -79,12 +88,13 @@ export const SaveTimelineModal = React.memo( ...titleAndDescription, }) ); - dispatch(timelineActions.saveTimeline({ id: timelineId })); + + dispatch(timelineActions.saveTimeline({ id: timelineId, saveAsNew: saveAsNewTimeline })); } return Promise.resolve(); }, - [dispatch, timelineId] + [dispatch, timelineId, saveAsNewTimeline] ); const initialState = useMemo( @@ -232,30 +242,40 @@ export const SaveTimelineModal = React.memo( - - - - {closeModalText} - - - - - {saveButtonTitle} - - + + {!isUnsaved ? ( + + ) : null} + + + + {closeModalText} + + + + + {saveButtonTitle} + + + diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/schema.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/schema.ts similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/schema.ts rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/schema.ts diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/translations.ts similarity index 70% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/translations.ts rename to x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/translations.ts index 83017597861a7..09b022d82faf2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/translations.ts @@ -9,6 +9,55 @@ import { i18n } from '@kbn/i18n'; import type { TimelineTypeLiteral } from '../../../../../common/api/timeline'; import { TimelineType } from '../../../../../common/api/timeline'; +export const NEW_TIMELINE_BTN = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.newTimelineBtn', + { + defaultMessage: 'New', + } +); + +export const NEW_TIMELINE = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.newTimeline', + { + defaultMessage: 'New Timeline', + } +); + +export const OPEN_TIMELINE_BTN = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.openTimelineBtn', + { + defaultMessage: 'Open', + } +); + +export const OPEN_TIMELINE_BTN_LABEL = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.openTimelineBtnLabel', + { + defaultMessage: 'Open Existing Timeline', + } +); + +export const SAVE_TIMELINE_BTN = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.saveTimelineBtn', + { + defaultMessage: 'Save', + } +); + +export const SAVE_TIMELINE_BTN_LABEL = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.saveTimelineBtnLabel', + { + defaultMessage: 'Save currently opened Timeline', + } +); + +export const NEW_TEMPLATE_TIMELINE = i18n.translate( + 'xpack.securitySolution.flyout.timeline.actionMenu.newTimelineTemplate', + { + defaultMessage: 'New Timeline template', + } +); + export const CALL_OUT_UNAUTHORIZED_MSG = i18n.translate( 'xpack.securitySolution.timeline.callOut.unauthorized.message.description', { @@ -79,13 +128,6 @@ export const UNSAVED_TIMELINE_WARNING = (timelineType: TimelineTypeLiteral) => defaultMessage: 'You have an unsaved {timeline}. Do you wish to save it?', }); -export const TITLE = i18n.translate( - 'xpack.securitySolution.timeline.saveTimeline.modal.titleTitle', - { - defaultMessage: 'Title', - } -); - export const TIMELINE_TITLE = i18n.translate( 'xpack.securitySolution.timeline.saveTimeline.modal.titleAriaLabel', { @@ -114,9 +156,20 @@ export const SAVE_TOUR_CLOSE = i18n.translate( } ); +export const TITLE = i18n.translate('xpack.securitySolution.timeline.saveTimeline.modal.title', { + defaultMessage: 'Title', +}); + export const SAVE_TOUR_TITLE = i18n.translate( 'xpack.securitySolution.timeline.flyout.saveTour.title', { defaultMessage: 'Timeline changes now require manual saves', } ); + +export const SAVE_AS_NEW = i18n.translate( + 'xpack.securitySolution.timeline.saveTimeline.modal.saveAsNew', + { + defaultMessage: 'Save as new timeline', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx index 74662e7563201..69b71adb9fb6e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx @@ -70,13 +70,13 @@ const AddTimelineButtonComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.test.tsx index 0e002561c0227..063d6da3f2d07 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.test.tsx @@ -10,7 +10,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; -import { useKibana, useGetUserCasesPermissions } from '../../../../common/lib/kibana'; +import { useKibana } from '../../../../common/lib/kibana'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { mockTimelineModel, TestProviders } from '../../../../common/mock'; import { AddToCaseButton } from '.'; @@ -36,13 +36,6 @@ jest.mock('react-redux', () => { }); jest.mock('../../../../common/lib/kibana'); -const originalKibanaLib = jest.requireActual('../../../../common/lib/kibana'); - -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); - jest.mock('../../../../common/hooks/use_selector'); const useKibanaMock = useKibana as jest.Mocked; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx index c25d48ad03dd9..81e520368da6c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx @@ -6,7 +6,7 @@ */ import { pick } from 'lodash/fp'; -import { EuiButton, EuiContextMenuPanel, EuiContextMenuItem, EuiPopover } from '@elastic/eui'; +import { EuiContextMenuPanel, EuiContextMenuItem, EuiPopover, EuiButtonEmpty } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; @@ -15,7 +15,7 @@ import { APP_ID, APP_UI_ID } from '../../../../../common/constants'; import { timelineSelectors } from '../../../store/timeline'; import { setInsertTimeline, showTimeline } from '../../../store/timeline/actions'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; import { getCreateCaseUrl, getCaseDetailsUrl } from '../../../../common/components/link_to'; @@ -68,7 +68,7 @@ const AddToCaseButtonComponent: React.FC = ({ timelineId }) => { [dispatch, graphEventId, navigateToApp, savedObjectId, timelineId, timelineTitle] ); - const userCasesPermissions = useGetUserCasesPermissions(); + const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); const handleButtonClick = useCallback(() => { setPopover((currentIsOpen) => !currentIsOpen); @@ -118,8 +118,7 @@ const AddToCaseButtonComponent: React.FC = ({ timelineId }) => { const button = useMemo( () => ( - = ({ timelineId }) => { disabled={timelineStatus === TimelineStatus.draft || timelineType !== TimelineType.default} > {i18n.ATTACH_TO_CASE} - + ), [handleButtonClick, timelineStatus, timelineType] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx index 0b5286f1fedb3..bc1617d3b9a53 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx @@ -10,6 +10,7 @@ import { FlyoutHeaderPanel } from '../header'; interface FlyoutBottomBarProps { showTimelineHeaderPanel: boolean; + timelineId: string; } diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.test.tsx new file mode 100644 index 0000000000000..18bf93d0ab6c8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.test.tsx @@ -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 { + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../../../common/mock'; +import React from 'react'; +import type { ActiveTimelinesProps } from './active_timelines'; +import { ActiveTimelines } from './active_timelines'; +import { TimelineId } from '../../../../../common/types'; +import { TimelineType } from '../../../../../common/api/timeline'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { createSecuritySolutionStorageMock } from '@kbn/timelines-plugin/public/mock/mock_local_storage'; +import { createStore } from '../../../../common/store'; + +const { storage } = createSecuritySolutionStorageMock(); + +const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + +const TestComponent = (props: ActiveTimelinesProps) => { + return ( + + + + ); +}; + +describe('ActiveTimelines', () => { + describe('default timeline', () => { + it('should render timeline title as button when minimized', () => { + render( + + ); + + expect(screen.getByLabelText(/Open timeline timeline-test/).nodeName.toLowerCase()).toBe( + 'button' + ); + }); + + it('should render timeline title as text when maximized', () => { + render( + + ); + expect(screen.queryByLabelText(/Open timeline timeline-test/)).toBeFalsy(); + }); + + it('should maximized timeline when clicked on minimized timeline', async () => { + render( + + ); + + fireEvent.click(screen.getByLabelText(/Open timeline timeline-test/)); + + await waitFor(() => { + expect(store.getState().timeline.timelineById.test.show).toBe(true); + }); + }); + }); + + describe('template timeline', () => { + it('should render timeline template title as button when minimized', () => { + render( + + ); + + expect(screen.getByTestId(/timeline-title/)).toHaveTextContent(/Untitled template/); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx index e6df4e56386a5..cbc38f4ccecb9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiHealth, EuiToolTip } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiText } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { isEmpty } from 'lodash/fp'; import styled from 'styled-components'; -import { FormattedRelative } from '@kbn/i18n-react'; -import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; +import { TimelineType } from '../../../../../common/api/timeline'; import { TimelineEventsCountBadge } from '../../../../common/hooks/use_timeline_events_count'; import { ACTIVE_TIMELINE_BUTTON_CLASS_NAME, @@ -22,20 +21,18 @@ import { UNTITLED_TIMELINE, UNTITLED_TEMPLATE } from '../../timeline/properties/ import { timelineActions } from '../../../store/timeline'; import * as i18n from './translations'; -const EuiHealthStyled = styled(EuiHealth)` - display: block; -`; - -interface ActiveTimelinesProps { +export interface ActiveTimelinesProps { timelineId: string; - timelineStatus: TimelineStatus; timelineTitle: string; timelineType: TimelineType; isOpen: boolean; - updated?: number; } const StyledEuiButtonEmpty = styled(EuiButtonEmpty)` + &:active, + &:focus { + background: transparent; + } > span { padding: 0; } @@ -45,17 +42,17 @@ const TitleConatiner = styled(EuiFlexItem)` overflow: hidden; display: inline-block; text-overflow: ellipsis; + white-space: nowrap; `; const ActiveTimelinesComponent: React.FC = ({ timelineId, - timelineStatus, timelineType, timelineTitle, - updated, isOpen, }) => { const dispatch = useDispatch(); + const handleToggleOpen = useCallback(() => { dispatch(timelineActions.showTimeline({ id: timelineId, show: !isOpen })); focusActiveTimelineButton(); @@ -67,53 +64,47 @@ const ActiveTimelinesComponent: React.FC = ({ ? UNTITLED_TEMPLATE : UNTITLED_TIMELINE; - const tooltipContent = useMemo(() => { - if (timelineStatus === TimelineStatus.draft) { - return <>{i18n.UNSAVED}; - } + const titleContent = useMemo(() => { return ( - <> - {i18n.SAVED}{' '} - - - ); - }, [timelineStatus, updated]); - - return ( - - - - - - - {title} + + {isOpen ? ( + +

    {title}

    +
    + ) : ( + <>{title} + )} +
    {!isOpen && ( )}
    + ); + }, [isOpen, title]); + + if (isOpen) { + return <>{titleContent}; + } + + return ( + + {titleContent} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx deleted file mode 100644 index 79ef41a070574..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx +++ /dev/null @@ -1,171 +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 React from 'react'; -import { render, screen } from '@testing-library/react'; - -import { useKibana, useGetUserCasesPermissions } from '../../../../common/lib/kibana'; -import { TestProviders, mockIndexNames, mockIndexPattern } from '../../../../common/mock'; -import { TimelineId } from '../../../../../common/types/timeline'; -import { useTimelineKpis } from '../../../containers/kpis'; -import { FlyoutHeader } from '.'; -import { useSourcererDataView } from '../../../../common/containers/sourcerer'; -import { mockBrowserFields } from '../../../../common/containers/source/mock'; -import { getEmptyValue } from '../../../../common/components/empty_value'; -import { allCasesPermissions, readCasesPermissions } from '../../../../cases_test_utils'; - -const mockUseSourcererDataView: jest.Mock = useSourcererDataView as jest.Mock; -jest.mock('../../../../common/containers/sourcerer'); - -const mockUseTimelineKpis: jest.Mock = useTimelineKpis as jest.Mock; -jest.mock('../../../containers/kpis', () => ({ - useTimelineKpis: jest.fn(), -})); -const useKibanaMock = useKibana as jest.Mocked; -jest.mock('../../../../common/lib/kibana'); -jest.mock('@kbn/i18n-react', () => { - const originalModule = jest.requireActual('@kbn/i18n-react'); - const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); - - return { - ...originalModule, - FormattedRelative, - }; -}); -const mockUseTimelineKpiResponse = { - processCount: 1, - userCount: 1, - sourceIpCount: 1, - hostCount: 1, - destinationIpCount: 1, -}; - -const mockUseTimelineLargeKpiResponse = { - processCount: 1000, - userCount: 1000000, - sourceIpCount: 1000000000, - hostCount: 999, - destinationIpCount: 1, -}; -const defaultMocks = { - browserFields: mockBrowserFields, - indexPattern: mockIndexPattern, - loading: false, - selectedPatterns: mockIndexNames, -}; -describe('header', () => { - beforeEach(() => { - // Mocking these services is required for the header component to render. - mockUseSourcererDataView.mockImplementation(() => defaultMocks); - useKibanaMock().services.application.capabilities = { - navLinks: {}, - management: {}, - catalogue: {}, - actions: { show: true, crud: true }, - }; - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('AddToCaseButton', () => { - beforeEach(() => { - mockUseTimelineKpis.mockReturnValue([false, mockUseTimelineKpiResponse]); - }); - - it('renders the button when the user has create and read permissions', () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(allCasesPermissions()); - - render( - - - - ); - - expect(screen.getByTestId('attach-timeline-case-button')).toBeInTheDocument(); - }); - - it('does not render the button when the user does not have create permissions', () => { - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(readCasesPermissions()); - - render( - - - - ); - - expect(screen.queryByTestId('attach-timeline-case-button')).not.toBeInTheDocument(); - }); - }); - - describe('Timeline KPIs', () => { - describe('when the data is not loading and the response contains data', () => { - beforeEach(() => { - mockUseTimelineKpis.mockReturnValue([false, mockUseTimelineKpiResponse]); - }); - it('renders the component, labels and values successfully', () => { - render( - - - - ); - expect(screen.getByTestId('siem-timeline-kpis')).toBeInTheDocument(); - // label - expect(screen.getByText('Processes')).toBeInTheDocument(); - // value - expect(screen.getByTestId('siem-timeline-process-kpi').textContent).toContain('1'); - }); - }); - - describe('when the data is loading', () => { - beforeEach(() => { - mockUseTimelineKpis.mockReturnValue([true, mockUseTimelineKpiResponse]); - }); - it('renders a loading indicator for values', async () => { - render( - - - - ); - expect(screen.getAllByText('--')).not.toHaveLength(0); - }); - }); - - describe('when the response is null and timeline is blank', () => { - beforeEach(() => { - mockUseTimelineKpis.mockReturnValue([false, null]); - }); - it('renders labels and the default empty string', () => { - render( - - - - ); - expect(screen.getByText('Processes')).toBeInTheDocument(); - expect(screen.getAllByText(getEmptyValue())).not.toHaveLength(0); - }); - }); - - describe('when the response contains numbers larger than one thousand', () => { - beforeEach(() => { - mockUseTimelineKpis.mockReturnValue([false, mockUseTimelineLargeKpiResponse]); - }); - it('formats the numbers correctly', () => { - render( - - - - ); - expect(screen.getByText('1k', { selector: '.euiTitle' })).toBeInTheDocument(); - expect(screen.getByText('1m', { selector: '.euiTitle' })).toBeInTheDocument(); - expect(screen.getByText('1b', { selector: '.euiTitle' })).toBeInTheDocument(); - expect(screen.getByText('999', { selector: '.euiTitle' })).toBeInTheDocument(); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 985d373950cc8..14158e884e1bb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -5,70 +5,49 @@ * 2.0. */ -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiToolTip, - EuiButtonIcon, - EuiText, - EuiButtonEmpty, - useEuiTheme, - EuiTextColor, -} from '@elastic/eui'; -import { FormattedRelative } from '@kbn/i18n-react'; -import type { MouseEventHandler } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiToolTip, EuiButtonIcon } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { isEmpty, get, pick } from 'lodash/fp'; import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; -import { InputsModelId } from '../../../../common/store/inputs/constants'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { createHistoryEntry } from '../../../../common/utils/global_query_string/helpers'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { TimelineTabs, TimelineId } from '../../../../../common/types/timeline'; -import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; -import type { State } from '../../../../common/store'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; -import { timelineDefaults } from '../../../store/timeline/defaults'; -import { AddToFavoritesButton } from '../../timeline/properties/helpers'; -import type { TimerangeInput } from '../../../../../common/search_strategy'; -import { AddToCaseButton } from '../add_to_case_button'; -import { AddTimelineButton } from '../add_timeline_button'; -import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana'; -import { InspectButton } from '../../../../common/components/inspect'; -import { useTimelineKpis } from '../../../containers/kpis'; +import type { State } from '../../../../common/store'; +import { useKibana } from '../../../../common/lib/kibana'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; -import type { TimelineModel } from '../../../store/timeline/model'; -import { - startSelector, - endSelector, -} from '../../../../common/components/super_date_picker/selectors'; import { focusActiveTimelineButton } from '../../timeline/helpers'; import { combineQueries } from '../../../../common/lib/kuery'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { ActiveTimelines } from './active_timelines'; import * as i18n from './translations'; -import * as commonI18n from '../../timeline/properties/translations'; -import { TimelineKPIs } from './kpis'; - -import { setActiveTabTimeline } from '../../../store/timeline/actions'; -import { useIsOverflow } from '../../../../common/hooks/use_is_overflow'; -import { SaveTimelineButton } from '../../timeline/header/save_timeline_button'; -import { TimelineSavePrompt } from '../../timeline/header/timeline_save_prompt'; - -interface FlyoutHeaderProps { - timelineId: string; -} +import { TimelineActionMenu } from '../action_menu'; +import { AddToFavoritesButton } from '../../timeline/properties/helpers'; +import { TimelineStatusInfo } from './timeline_status_info'; +import { timelineDefaults } from '../../../store/timeline/defaults'; interface FlyoutHeaderPanelProps { timelineId: string; } +const FlyoutHeaderPanelContentFlexGroupContainer = styled(EuiFlexGroup)` + overflow-x: auto; +`; + const ActiveTimelinesContainer = styled(EuiFlexItem)` overflow: hidden; `; +const TimelinePanel = euiStyled(EuiPanel)<{ $isOpen?: boolean }>` + backgroundColor: ${(props) => props.theme.eui.euiColorEmptyShade}; + color: ${(props) => props.theme.eui.euiTextColor}; + padding-inline: ${(props) => props.theme.eui.euiSizeM}; + border-radius: ${({ $isOpen, theme }) => ($isOpen ? theme.eui.euiBorderRadius : '0px')}; +`; + const FlyoutHeaderPanelComponent: React.FC = ({ timelineId }) => { const dispatch = useDispatch(); const { browserFields, indexPattern } = useSourcererDataView(SourcererScopeName.timeline); @@ -86,6 +65,7 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline show, filters, kqlMode, + changed = false, } = useDeepEqualSelector((state) => pick( [ @@ -99,6 +79,7 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline 'show', 'filters', 'kqlMode', + 'changed', ], getTimeline(state, timelineId) ?? timelineDefaults ) @@ -109,14 +90,15 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline ); const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)!); + + const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)); const kqlQueryExpression = isEmpty(dataProviders) && isEmpty(kqlQueryTimeline) && timelineType === 'template' ? ' ' - : kqlQueryTimeline; - const kqlQueryTest = useMemo( + : kqlQueryTimeline ?? ''; + + const kqlQueryObj = useMemo( () => ({ query: kqlQueryExpression, language: 'kuery' }), [kqlQueryExpression] ); @@ -129,55 +111,69 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline indexPattern, browserFields, filters: filters ? filters : [], - kqlQuery: kqlQueryTest, + kqlQuery: kqlQueryObj, kqlMode, }), - [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQueryTest] + [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQueryObj] ); const handleClose = useCallback(() => { + createHistoryEntry(); dispatch(timelineActions.showTimeline({ id: timelineId, show: false })); focusActiveTimelineButton(); }, [dispatch, timelineId]); - const { euiTheme } = useEuiTheme(); - return ( - - - - - - + + + + + + + + + + + + + + + + {show && ( - - - {(activeTab === TimelineTabs.query || activeTab === TimelineTabs.eql) && ( - - - - )} + + + = ({ timeline )} - - + + ); }; export const FlyoutHeaderPanel = React.memo(FlyoutHeaderPanelComponent); - -const StyledDiv = styled.div` - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - overflow: hidden; - word-break: break-word; -`; - -const ReadMoreButton = ({ - description, - onclick, -}: { - description: string; - onclick: MouseEventHandler; -}) => { - const [isOverflow, ref] = useIsOverflow(description); - return ( - <> - {description} - {isOverflow && ( - - {i18n.READ_MORE} - - )} - - ); -}; - -const StyledTimelineHeader = styled(EuiFlexGroup)` - ${({ theme }) => `margin: ${theme.eui.euiSizeXS} ${theme.eui.euiSizeS} 0 ${theme.eui.euiSizeS};`} - flex: 0; -`; - -const TimelineStatusInfoContainer = styled.span` - ${({ theme }) => `margin-left: ${theme.eui.euiSizeS};`} - white-space: nowrap; -`; - -const KpisContainer = styled.div` - ${({ theme }) => `margin-right: ${theme.eui.euiSizeM};`} -`; - -const RowFlexItem = styled(EuiFlexItem)` - flex-direction: row; - align-items: center; -`; - -const TimelineTitleContainer = styled.h3` - display: -webkit-box; - overflow: hidden; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - word-break: break-word; -`; - -const TimelineNameComponent: React.FC = ({ timelineId }) => { - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); - const { title, timelineType } = useDeepEqualSelector((state) => - pick(['title', 'timelineType'], getTimeline(state, timelineId) ?? timelineDefaults) - ); - const placeholder = useMemo( - () => - timelineType === TimelineType.template - ? commonI18n.UNTITLED_TEMPLATE - : commonI18n.UNTITLED_TIMELINE, - [timelineType] - ); - - const content = useMemo(() => title || placeholder, [title, placeholder]); - - return ( - - - {content} - - - ); -}; - -const TimelineName = React.memo(TimelineNameComponent); - -const TimelineDescriptionComponent: React.FC<{ timelineId: string; description?: string }> = ({ - timelineId, - description, -}) => { - const dispatch = useDispatch(); - - const onReadMore = useCallback(() => { - dispatch( - setActiveTabTimeline({ - id: timelineId, - activeTab: TimelineTabs.notes, - scrollToTop: true, - }) - ); - }, [dispatch, timelineId]); - - const hasDescription = !!description; - return hasDescription ? ( - - - - ) : null; -}; - -const TimelineDescription = React.memo(TimelineDescriptionComponent); - -const TimelineStatusInfoComponent = React.memo<{ - status: TimelineStatus; - updated?: number; - changed?: boolean; -}>(({ status, updated, changed }) => { - const isUnsaved = status === TimelineStatus.draft; - - let statusContent: React.ReactNode = null; - if (isUnsaved) { - statusContent = {i18n.UNSAVED}; - } else if (changed) { - statusContent = {i18n.UNSAVED_CHANGES}; - } else { - statusContent = ( - <> - {i18n.SAVED}{' '} - - - ); - } - return ( - - {statusContent} - - ); -}); -TimelineStatusInfoComponent.displayName = 'TimelineStatusInfoComponent'; - -const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { - const { selectedPatterns, indexPattern, browserFields } = useSourcererDataView( - SourcererScopeName.timeline - ); - const getStartSelector = useMemo(() => startSelector(), []); - const getEndSelector = useMemo(() => endSelector(), []); - const isActive = useMemo(() => timelineId === TimelineId.active, [timelineId]); - const timerange: TimerangeInput = useDeepEqualSelector((state) => { - if (isActive) { - return { - from: getStartSelector(state.inputs.timeline), - to: getEndSelector(state.inputs.timeline), - interval: '', - }; - } else { - return { - from: getStartSelector(state.inputs.global), - to: getEndSelector(state.inputs.global), - interval: '', - }; - } - }); - const { uiSettings } = useKibana().services; - const esQueryConfig = useMemo(() => getEsQueryConfig(uiSettings), [uiSettings]); - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); - const timeline: TimelineModel = useSelector( - (state: State) => getTimeline(state, timelineId) ?? timelineDefaults - ); - const { dataProviders, filters, timelineType, kqlMode, activeTab } = timeline; - const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)!); - - const kqlQueryExpression = - isEmpty(dataProviders) && isEmpty(kqlQueryTimeline) && timelineType === 'template' - ? ' ' - : kqlQueryTimeline; - const kqlQuery = useMemo( - () => ({ query: kqlQueryExpression, language: 'kuery' }), - [kqlQueryExpression] - ); - - const combinedQueries = useMemo( - () => - combineQueries({ - config: esQueryConfig, - dataProviders, - indexPattern, - browserFields, - filters: filters ? filters : [], - kqlQuery, - kqlMode, - }), - [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQuery] - ); - - const isBlankTimeline: boolean = useMemo( - () => - (isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query)) || - combinedQueries?.filterQuery === undefined, - [dataProviders, filters, kqlQuery, combinedQueries] - ); - - const [loading, kpis] = useTimelineKpis({ - defaultIndex: selectedPatterns, - timerange, - isBlankTimeline, - filterQuery: combinedQueries?.filterQuery ?? '', - }); - - const userCasesPermissions = useGetUserCasesPermissions(); - return ( - - - - - - - - - - - - - - - - - - - {activeTab === TimelineTabs.query ? ( - - ) : null} - - - - - - - - - {userCasesPermissions.create && userCasesPermissions.read && ( - - - - )} - - - - - - - ); -}; - -FlyoutHeaderComponent.displayName = 'FlyoutHeaderComponent'; - -export const FlyoutHeader = React.memo(FlyoutHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/kpis.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/kpis.tsx deleted file mode 100644 index 4cb622ec801b4..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/kpis.tsx +++ /dev/null @@ -1,110 +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 React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { EuiStat, EuiFlexItem, EuiFlexGroup, EuiToolTip } from '@elastic/eui'; -import numeral from '@elastic/numeral'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; -import { useUiSetting$ } from '../../../../common/lib/kibana'; -import type { TimelineKpiStrategyResponse } from '../../../../../common/search_strategy'; -import { getEmptyValue } from '../../../../common/components/empty_value'; -import * as i18n from './translations'; - -const NoWrapEuiStat = styled(EuiStat)` - & .euiStat__description { - white-space: nowrap; - } -`; - -export const TimelineKPIs = React.memo( - ({ kpis, isLoading }: { kpis: TimelineKpiStrategyResponse | null; isLoading: boolean }) => { - const kpiFormat = '0,0.[000]a'; - const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const formattedKpis = useMemo(() => { - return { - process: kpis === null ? getEmptyValue() : numeral(kpis.processCount).format(kpiFormat), - user: kpis === null ? getEmptyValue() : numeral(kpis.userCount).format(kpiFormat), - host: kpis === null ? getEmptyValue() : numeral(kpis.hostCount).format(kpiFormat), - sourceIp: kpis === null ? getEmptyValue() : numeral(kpis.sourceIpCount).format(kpiFormat), - destinationIp: - kpis === null ? getEmptyValue() : numeral(kpis.destinationIpCount).format(kpiFormat), - }; - }, [kpis]); - const formattedKpiToolTips = useMemo(() => { - return { - process: numeral(kpis?.processCount).format(defaultNumberFormat), - user: numeral(kpis?.userCount).format(defaultNumberFormat), - host: numeral(kpis?.hostCount).format(defaultNumberFormat), - sourceIp: numeral(kpis?.sourceIpCount).format(defaultNumberFormat), - destinationIp: numeral(kpis?.destinationIpCount).format(defaultNumberFormat), - }; - }, [kpis, defaultNumberFormat]); - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } -); - -TimelineKPIs.displayName = 'TimelineKPIs'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx new file mode 100644 index 0000000000000..f7fda862f793f --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx @@ -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 React from 'react'; +import { screen, render } from '@testing-library/react'; +import type { TimelineStatusInfoProps } from './timeline_status_info'; +import { TimelineStatusInfo } from './timeline_status_info'; +import { TimelineStatus } from '../../../../../common/api/timeline'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +const TestComponent = (props: TimelineStatusInfoProps) => { + return ( + + + + ); +}; + +describe('TestComponent', () => { + it('should render the status correctly when timeline is unsaved', () => { + render(); + expect(screen.getByText('Unsaved')).toBeVisible(); + }); + + it('should render the status correctly when timeline has unsaved changes', () => { + render(); + expect(screen.getByText('Has unsaved changes')).toBeVisible(); + }); + + it('should render the status correctly when timeline is saved', () => { + const updatedTime = Date.now(); + render(); + expect(screen.getByText('Saved')).toBeVisible(); + }); + + it('should render the status correctly when timeline is saved some time ago', () => { + const updatedTime = Date.now() - 10000; + render(); + expect(screen.getByTestId('timeline-status')).toHaveTextContent(/Saved10 seconds ago/); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx new file mode 100644 index 0000000000000..ed164ddab47fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx @@ -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 React from 'react'; +import { EuiTextColor, EuiText } from '@elastic/eui'; +import { FormattedRelative } from '@kbn/i18n-react'; + +import styled from 'styled-components'; +import { TimelineStatus } from '../../../../../common/api/timeline'; +import * as i18n from './translations'; + +const NoWrapText = styled(EuiText)` + white-space: nowrap; +`; + +export interface TimelineStatusInfoProps { + status: TimelineStatus; + updated?: number; + changed?: boolean; +} + +export const TimelineStatusInfo = React.memo( + ({ status, updated, changed }) => { + const isUnsaved = status === TimelineStatus.draft; + + let statusContent: React.ReactNode = null; + if (isUnsaved || !updated) { + statusContent = {i18n.UNSAVED}; + } else if (changed) { + statusContent = {i18n.UNSAVED_CHANGES}; + } else { + statusContent = ( + <> + {i18n.SAVED} + + + ); + } + return ( + + {statusContent} + + ); + } +); +TimelineStatusInfo.displayName = 'TimelineStatusInfo'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts index 2a52d9407ad58..56036f899e61f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts @@ -37,35 +37,6 @@ export const INSPECT_TIMELINE_TITLE = i18n.translate( } ); -export const PROCESS_KPI_TITLE = i18n.translate( - 'xpack.securitySolution.timeline.kpis.processKpiTitle', - { - defaultMessage: 'Processes', - } -); - -export const HOST_KPI_TITLE = i18n.translate('xpack.securitySolution.timeline.kpis.hostKpiTitle', { - defaultMessage: 'Hosts', -}); - -export const SOURCE_IP_KPI_TITLE = i18n.translate( - 'xpack.securitySolution.timeline.kpis.sourceIpKpiTitle', - { - defaultMessage: 'Source IPs', - } -); - -export const DESTINATION_IP_KPI_TITLE = i18n.translate( - 'xpack.securitySolution.timeline.kpis.destinationKpiTitle', - { - defaultMessage: 'Destination IPs', - } -); - -export const USER_KPI_TITLE = i18n.translate('xpack.securitySolution.timeline.kpis.userKpiTitle', { - defaultMessage: 'Users', -}); - export const READ_MORE = i18n.translate('xpack.securitySolution.timeline.properties.readMore', { defaultMessage: 'Read More', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/pane.styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/pane.styles.tsx index 6824756f8bb0c..95bea7d742ca1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/pane.styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/pane.styles.tsx @@ -68,9 +68,6 @@ export const usePaneStyles = () => { .timeline-template-badge { border-radius: ${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium} 0 0; // top corners only } - .timeline-body { - padding: 0 ${euiTheme.size.s}; - } } `; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/notes/add_note/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/notes/add_note/index.tsx index 31df17fa40046..f770d3a46c46d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/notes/add_note/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/notes/add_note/index.tsx @@ -33,7 +33,7 @@ const AddNotesContainer = styled.div` AddNotesContainer.displayName = 'AddNotesContainer'; const ButtonsContainer = styled(EuiFlexGroup)` - margin-top: 5px; + margin-top: ${({ theme }) => theme.eui.euiSizeS}; `; ButtonsContainer.displayName = 'ButtonsContainer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index 6fcc467c7626b..f0e4e31f1e9be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -122,16 +122,24 @@ Array [ />

    , -
    + class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexEnd-stretch-row" + > +
    +
    , ] @@ -164,6 +172,10 @@ exports[`Details Panel Component DetailsPanel:EventDetails: rendering it should padding: 0 12px 12px; } +.c2 .side-panel-flyout-footer { + background-color: transparent; +} +
    + class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexEnd-stretch-row" + > +
    +
    diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx index 91a2904287ffc..5362af38c2413 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx @@ -13,11 +13,7 @@ import { TimelineId } from '../../../../../../common/types/timeline'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { mockAlertDetailsData } from '../../../../../common/components/event_details/__mocks__'; import type { TimelineEventsDetailsItem } from '../../../../../../common/search_strategy'; -import { - KibanaServices, - useGetUserCasesPermissions, - useKibana, -} from '../../../../../common/lib/kibana'; +import { KibanaServices, useKibana } from '../../../../../common/lib/kibana'; import { coreMock } from '@kbn/core/public/mocks'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; @@ -70,12 +66,6 @@ jest.mock('../../../../../detections/components/user_info', () => ({ })); jest.mock('../../../../../common/lib/kibana'); -const originalKibanaLib = jest.requireActual('../../../../../common/lib/kibana'); - -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); jest.mock( '../../../../../detections/containers/detection_engine/alerts/use_alerts_privileges', diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.tsx index ac5d5ee32797c..15d6b2040234a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.tsx @@ -142,7 +142,10 @@ export const FlyoutFooterComponent = React.memo( return ( <> - + {detailsEcsData && ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx index 10a536f69c8d0..508a5caa590f4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx @@ -11,11 +11,7 @@ import '../../../../common/mock/match_media'; import { TestProviders } from '../../../../common/mock'; import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; -import { - KibanaServices, - useKibana, - useGetUserCasesPermissions, -} from '../../../../common/lib/kibana'; +import { KibanaServices, useKibana } from '../../../../common/lib/kibana'; import { mockBrowserFields, mockRuntimeMappings } from '../../../../common/containers/source/mock'; import { coreMock } from '@kbn/core/public/mocks'; import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context'; @@ -156,6 +152,9 @@ describe('event details panel component', () => { ui: { getCasesContext: () => mockCasesContext, }, + cases: { + helpers: { canUseCases: jest.fn().mockReturnValue(allCasesPermissions()) }, + }, }, timelines: { getHoverActions: jest.fn().mockReturnValue({ @@ -168,11 +167,12 @@ describe('event details panel component', () => { }, }, }); - (useGetUserCasesPermissions as jest.Mock).mockReturnValue(allCasesPermissions()); }); + afterEach(() => { jest.clearAllMocks(); }); + test('it renders the take action dropdown in the timeline version', () => { const wrapper = render( diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 729b20e68cb15..a2a45e46a07b2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -6,8 +6,9 @@ */ import { useAssistantOverlay } from '@kbn/elastic-assistant'; -import { EuiSpacer, EuiFlyoutBody } from '@elastic/eui'; +import { EuiSpacer, EuiFlyoutBody, EuiPanel } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; import type { EntityType } from '@kbn/timelines-plugin/common'; @@ -41,6 +42,12 @@ import { PROMPT_CONTEXTS, } from '../../../../assistant/content/prompt_contexts'; +const FlyoutFooterContainerPanel = styled(EuiPanel)` + .side-panel-flyout-footer { + background-color: transparent; + } +`; + interface EventDetailsPanelProps { browserFields: BrowserFields; entityType?: EntityType; @@ -254,17 +261,19 @@ const EventDetailsPanelComponent: React.FC = ({ <> {header} {body} - + + + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx index 59c9aa8e24c06..c712c1b5c8df5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx @@ -6,10 +6,12 @@ */ import { rgba } from 'polished'; -import React, { useMemo } from 'react'; +import { useDispatch } from 'react-redux'; +import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { v4 as uuidv4 } from 'uuid'; import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; +import { EuiToolTip, EuiSuperSelect, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; @@ -23,13 +25,16 @@ import { timelineSelectors } from '../../../store/timeline'; import { timelineDefaults } from '../../../store/timeline/defaults'; import * as i18n from './translations'; +import { options } from '../search_or_filter/helpers'; +import type { KqlMode } from '../../../store/timeline/model'; +import { updateKqlMode } from '../../../store/timeline/actions'; interface Props { timelineId: string; } const DropTargetDataProvidersContainer = styled.div` - padding: 2px 0 4px 0; + position: relative; .${IS_DRAGGING_CLASS_NAME} & .drop-target-data-providers { background: ${({ theme }) => rgba(theme.eui.euiColorSuccess, 0.1)}; @@ -49,12 +54,11 @@ const DropTargetDataProviders = styled.div` display: flex; flex-direction: column; justify-content: flex-start; - padding-bottom: 2px; position: relative; border: 0.2rem dashed ${({ theme }) => theme.eui.euiColorMediumShade}; border-radius: 5px; - padding: ${({ theme }) => theme.eui.euiSizeXS} 0; - margin: 2px 0 2px 0; + padding: ${({ theme }) => theme.eui.euiSizeS} 0; + margin: 0px 0 0px 0; max-height: 33vh; min-height: 100px; overflow: auto; @@ -84,7 +88,24 @@ const getDroppableId = (id: string): string => * the user to drop anything with a facet count into * the data pro section. */ + +const timelineSelectModeItemsClassName = 'timelineSelectModeItemsClassName'; + +const searchOrFilterPopoverClassName = 'searchOrFilterPopover'; +const searchOrFilterPopoverWidth = 350; + +const popoverProps = { + className: searchOrFilterPopoverClassName, + panelClassName: searchOrFilterPopoverClassName, + panelMinWidth: searchOrFilterPopoverWidth, +}; + +const CustomTooltipDiv = styled.div` + position: relative; +`; + export const DataProviders = React.memo(({ timelineId }) => { + const dispatch = useDispatch(); const { browserFields } = useSourcererDataView(SourcererScopeName.timeline); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -96,28 +117,65 @@ export const DataProviders = React.memo(({ timelineId }) => { ); const droppableId = useMemo(() => getDroppableId(timelineId), [timelineId]); + const kqlMode = useDeepEqualSelector( + (state) => (getTimeline(state, timelineId) ?? timelineDefaults).kqlMode + ); + + const handleChange = useCallback( + (mode: KqlMode) => { + dispatch(updateKqlMode({ id: timelineId, kqlMode: mode })); + }, + [timelineId, dispatch] + ); + return ( - - + - {dataProviders != null && dataProviders.length ? ( - - ) : ( - - - - )} - - + + + + + + + + + + + {dataProviders != null && dataProviders.length ? ( + + ) : ( + + + + )} + + + + + ); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx index 9de471e578363..5d581a505742f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx @@ -66,8 +66,10 @@ const getItemStyle = ( const DroppableContainer = styled.div` min-height: ${ROW_OF_DATA_PROVIDERS_HEIGHT}px; height: auto !important; + display: none; .${IS_DRAGGING_CLASS_NAME} &:hover { + display: flex; background-color: ${({ theme }) => rgba(theme.eui.euiColorSuccess, 0.2)} !important; } `; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/translations.ts index 18ca62a71c6d1..8733e42d24966 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/translations.ts @@ -169,3 +169,10 @@ export const GROUP_AREA_ARIA_LABEL = (group: number) => values: { group }, defaultMessage: 'You are in group {group}', }); + +export const FILTER_OR_SEARCH_WITH_KQL = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.filterOrSearchWithKql', + { + defaultMessage: 'Filter or Search with KQL', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/date_picker_lock/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/date_picker_lock/index.tsx index c3d3db91430ee..4335e0b806a1b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/date_picker_lock/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/date_picker_lock/index.tsx @@ -39,8 +39,10 @@ const TimelineDatePickerLockComponent = () => { props.theme.eui.euiSizeS}; width: 100%; `; -TimelineHeaderContainer.displayName = 'TimelineHeaderContainer'; +EqlTabHeaderContainer.displayName = 'EqlTabHeaderContainer'; const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` align-items: stretch; @@ -73,8 +73,7 @@ const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` padding: 0; &.euiFlyoutHeader { - ${({ theme }) => - `padding: 0 ${theme.eui.euiSizeM} ${theme.eui.euiSizeS} ${theme.eui.euiSizeS};`} + ${({ theme }) => `padding: ${theme.eui.euiSizeS} 0 0 0;`} } `; @@ -110,6 +109,7 @@ const FullWidthFlexGroup = styled(EuiFlexGroup)` `; const ScrollableFlexItem = styled(EuiFlexItem)` + ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} overflow: hidden; `; @@ -260,9 +260,11 @@ export const EqlTabContentComponent: React.FC = ({ hasBorder={false} > {timelineFullScreen && setTimelineFullScreen != null && ( = ({ setFullScreen={setTimelineFullScreen} /> )} - - - - - - {activeTab === TimelineTabs.eql && ( )} + + + + + + - - - + + + = ({ timelineId }) setDiscoverStateContainer, getAppStateFromSavedSearch, updateSavedSearch, + initializeLocalSavedSearch, restoreDiscoverAppStateFromSavedSearch, resetDiscoverAppState, getDefaultDiscoverAppState, @@ -170,11 +171,6 @@ export const DiscoverTabContent: FC = ({ timelineId }) const combinedDiscoverSavedSearchStateRef = useRef(); - const debouncedUpdateSavedSearch = useMemo( - () => debounce(updateSavedSearch, 300), - [updateSavedSearch] - ); - useEffect(() => { if (isFetching) return; if (!isDiscoverSavedSearchLoaded) return; @@ -187,11 +183,10 @@ export const DiscoverTabContent: FC = ({ timelineId }) if (!index) return; if (!latestState || combinedDiscoverSavedSearchStateRef.current === latestState) return; if (isEqualWith(latestState, savedSearchById, savedSearchComparator)) return; - debouncedUpdateSavedSearch(latestState, timelineId); + updateSavedSearch(latestState, timelineId); combinedDiscoverSavedSearchStateRef.current = latestState; }, [ getCombinedDiscoverSavedSearchState, - debouncedUpdateSavedSearch, savedSearchById, updateSavedSearch, isDiscoverSavedSearchLoaded, @@ -230,6 +225,7 @@ export const DiscoverTabContent: FC = ({ timelineId }) let savedSearchAppState; if (savedSearchId) { const localSavedSearch = await savedSearchService.get(savedSearchId); + initializeLocalSavedSearch(localSavedSearch, timelineId); savedSearchAppState = getAppStateFromSavedSearch(localSavedSearch); } @@ -299,6 +295,8 @@ export const DiscoverTabContent: FC = ({ timelineId }) savedSearchId, savedSearchService, getDefaultDiscoverAppState, + timelineId, + initializeLocalSavedSearch, ] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.test.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.test.ts index 3b25737b25278..bc7cb3e2f8a0a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.test.ts @@ -14,57 +14,32 @@ const customQuery = { query: '_id: *', }; -const firstDataViewMock = buildDataViewMock({ +const dataViewMock = buildDataViewMock({ name: 'first-data-view', fields: shallowMockedFields, }); -const secondDataViewMock = buildDataViewMock({ - name: 'second-data-view', - fields: shallowMockedFields, -}); - describe('savedSearchComparator', () => { - const firstMockSavedSearch = { + const mockSavedSearch = { id: 'first', title: 'first title', breakdownField: 'firstBreakdown Field', searchSource: createSearchSourceMock({ - index: firstDataViewMock, + index: dataViewMock, query: customQuery, }), }; - const secondMockSavedSearch = { - id: 'second', - title: 'second title', - breakdownField: 'second Breakdown Field', - searchSource: createSearchSourceMock({ - index: secondDataViewMock, - query: customQuery, - }), - }; it('should result true when saved search is same', () => { - const result = savedSearchComparator(firstMockSavedSearch, { ...firstMockSavedSearch }); + const result = savedSearchComparator(mockSavedSearch, { ...mockSavedSearch }); expect(result).toBe(true); }); - it('should return false index is different', () => { - const newMockedSavedSearch = { - ...firstMockSavedSearch, - searchSource: secondMockSavedSearch.searchSource, - }; - - const result = savedSearchComparator(firstMockSavedSearch, newMockedSavedSearch); - - expect(result).toBe(false); - }); - it('should return false when query is different', () => { const newMockedSavedSearch = { - ...firstMockSavedSearch, + ...mockSavedSearch, searchSource: createSearchSourceMock({ - index: firstDataViewMock, + index: dataViewMock, query: { ...customQuery, query: '*', @@ -72,17 +47,17 @@ describe('savedSearchComparator', () => { }), }; - const result = savedSearchComparator(firstMockSavedSearch, newMockedSavedSearch); + const result = savedSearchComparator(mockSavedSearch, newMockedSavedSearch); expect(result).toBe(false); }); it('should result false when title is different', () => { const newMockedSavedSearch = { - ...firstMockSavedSearch, + ...mockSavedSearch, title: 'new-title', }; - const result = savedSearchComparator(firstMockSavedSearch, newMockedSavedSearch); + const result = savedSearchComparator(mockSavedSearch, newMockedSavedSearch); expect(result).toBe(false); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.ts index 26340c12add52..1f908f5a5fe6f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/utils/index.ts @@ -29,7 +29,6 @@ export const savedSearchComparator = ( 'sort', 'timeRange', 'fields.filter', - 'fields.index.id', 'fields.query', 'title', 'description', diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 8ea295a03c9a9..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,51 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header rendering renders correctly against snapshot 1`] = ` - - - - -`; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx deleted file mode 100644 index 56936c1840ba2..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx +++ /dev/null @@ -1,59 +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 { EuiCallOut } from '@elastic/eui'; -import React from 'react'; -import type { FilterManager } from '@kbn/data-plugin/public'; - -import { DataProviders } from '../data_providers'; -import { StatefulSearchOrFilter } from '../search_or_filter'; - -import * as i18n from './translations'; -import type { TimelineStatusLiteralWithNull } from '../../../../../common/api/timeline'; -import { TimelineStatus } from '../../../../../common/api/timeline'; - -interface Props { - filterManager: FilterManager; - show: boolean; - showCallOutUnauthorizedMsg: boolean; - status: TimelineStatusLiteralWithNull; - timelineId: string; -} - -const TimelineHeaderComponent: React.FC = ({ - filterManager, - show, - showCallOutUnauthorizedMsg, - status, - timelineId, -}) => ( - <> - {showCallOutUnauthorizedMsg && ( - - )} - {status === TimelineStatus.immutable && ( - - )} - {show && } - - - -); - -export const TimelineHeader = React.memo(TimelineHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/timeline_save_prompt.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/timeline_save_prompt.tsx deleted file mode 100644 index fdf3e453c8f65..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/timeline_save_prompt.tsx +++ /dev/null @@ -1,59 +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 React, { useCallback, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; -import { useUserPrivileges } from '../../../../common/components/user_privileges'; - -import { TimelineId } from '../../../../../common/types/timeline'; -import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { timelineActions } from '../../../store/timeline'; -import { getTimelineSaveModalByIdSelector } from './selectors'; -import { SaveTimelineModal } from './save_timeline_modal'; -import { TimelineStatus } from '../../../../../common/api/timeline'; - -interface TimelineSavePromptProps { - timelineId: string; -} - -/** - * Displays the edit timeline modal with a warning that unsaved changes might get lost. - * The modal is rendered based on a flag that is set in Redux, in other words, this component - * only renders the modal when the flag is triggered from outside this component. - */ -export const TimelineSavePrompt = React.memo(({ timelineId }) => { - const dispatch = useDispatch(); - const getTimelineSaveModal = useMemo(() => getTimelineSaveModalByIdSelector(), []); - const { showSaveModal: forceShow, status } = useDeepEqualSelector((state) => - getTimelineSaveModal(state, timelineId) - ); - const isUnsaved = status === TimelineStatus.draft; - - const closeSaveTimeline = useCallback(() => { - dispatch( - timelineActions.toggleModalSaveTimeline({ - id: TimelineId.active, - showModalSaveTimeline: false, - }) - ); - }, [dispatch]); - - const { - kibanaSecuritySolutionsPrivileges: { crud: hasKibanaCrud }, - } = useUserPrivileges(); - - return forceShow && hasKibanaCrud ? ( - - ) : null; -}); - -TimelineSavePrompt.displayName = 'TimelineSavePrompt'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index ce03812e58fc8..7928a93dc4885 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -32,7 +32,6 @@ import { defaultRowRenderers } from './body/renderers'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { createStore } from '../../../common/store'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; -import { useGetUserCasesPermissions } from '../../../common/lib/kibana'; jest.mock('../../containers', () => ({ useTimelineEvents: jest.fn(), @@ -43,12 +42,6 @@ jest.mock('./tabs_content', () => ({ })); jest.mock('../../../common/lib/kibana'); -const originalKibanaLib = jest.requireActual('../../../common/lib/kibana'); - -// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object -// The returned permissions object will indicate that the user does not have permissions by default -const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; -mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); jest.mock('../../../common/utils/normalize_time_range'); jest.mock('@kbn/i18n-react', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 7ebd3093d8cfc..a78747e8cb32f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -17,7 +17,7 @@ import { timelineDefaults } from '../../store/timeline/defaults'; import { defaultHeaders } from './body/column_headers/default_headers'; import type { CellValueElementProps } from './cell_rendering'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; -import { FlyoutHeader, FlyoutHeaderPanel } from '../flyout/header'; +import { FlyoutHeaderPanel } from '../flyout/header'; import type { TimelineId, RowRenderer } from '../../../../common/types/timeline'; import { TimelineType } from '../../../../common/api/timeline'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; @@ -192,19 +192,18 @@ const StatefulTimelineComponent: React.FC = ({ ref={containerElement} > - {timelineType === TimelineType.template && ( - - {i18n.TIMELINE_TEMPLATE} - - )}
    + {timelineType === TimelineType.template && ( + + {i18n.TIMELINE_TEMPLATE} + + )} {resolveConflictComponent} - ({ + useTimelineKpis: jest.fn(), +})); + +jest.mock('../../../../common/lib/kibana'); + +jest.mock('@kbn/i18n-react', () => { + const originalModule = jest.requireActual('@kbn/i18n-react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); + +const mockUseTimelineKpis: jest.Mock = useTimelineKpis as jest.Mock; + +const mockUseTimelineKpiResponse = { + processCount: 1, + userCount: 1, + sourceIpCount: 1, + hostCount: 1, + destinationIpCount: 1, +}; + +const mockUseTimelineLargeKpiResponse = { + processCount: 1000, + userCount: 1000000, + sourceIpCount: 1000000000, + hostCount: 999, + destinationIpCount: 1, +}; + +describe('Timeline KPIs', () => { + describe('when the data is not loading and the response contains data', () => { + beforeEach(() => { + mockUseTimelineKpis.mockReturnValue([false, mockUseTimelineKpiResponse]); + }); + it('renders the component, labels and values successfully', () => { + render( + + + + ); + expect(screen.getByTestId('siem-timeline-kpis')).toBeInTheDocument(); + // label + expect(screen.getByText('Processes :')).toBeInTheDocument(); + // value + expect(screen.getByTestId('siem-timeline-process-kpi').textContent).toContain('1'); + }); + }); + + describe('when the response is null and timeline is blank', () => { + beforeEach(() => { + mockUseTimelineKpis.mockReturnValue([false, null]); + }); + it('renders labels and the default empty string', () => { + render( + + + + ); + expect(screen.getByText('Processes :')).toBeInTheDocument(); + expect(screen.getAllByText(getEmptyValue())).not.toHaveLength(0); + }); + }); + + describe('when the response contains numbers larger than one thousand', () => { + beforeEach(() => { + mockUseTimelineKpis.mockReturnValue([false, mockUseTimelineLargeKpiResponse]); + }); + it('formats the numbers correctly', () => { + render( + + + + ); + expect(screen.getByTitle('1k')).toBeInTheDocument(); + expect(screen.getByTitle('1m')).toBeInTheDocument(); + expect(screen.getByTitle('1b')).toBeInTheDocument(); + expect(screen.getByTitle('999')).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/index.tsx new file mode 100644 index 0000000000000..a0ebf4d5408f7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { TimelineKpisContainer as TimelineKpi } from './kpi_container'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx new file mode 100644 index 0000000000000..31eb9ad5e5e53 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { isEmpty, pick } from 'lodash/fp'; +import { useSelector } from 'react-redux'; +import { getEsQueryConfig } from '@kbn/data-plugin/common'; +import type { TimerangeInput } from '@kbn/timelines-plugin/common'; +import { EuiPanel } from '@elastic/eui'; +import { TimelineId } from '../../../../../common/types'; +import { useSourcererDataView } from '../../../../common/containers/sourcerer'; +import type { State } from '../../../../common/store'; +import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { TimelineKPIs } from './kpis'; +import { useTimelineKpis } from '../../../containers/kpis'; +import { useKibana } from '../../../../common/lib/kibana'; +import { timelineSelectors } from '../../../store/timeline'; +import { timelineDefaults } from '../../../store/timeline/defaults'; +import { combineQueries } from '../../../../common/lib/kuery'; +import { + endSelector, + startSelector, +} from '../../../../common/components/super_date_picker/selectors'; + +interface KpiExpandedProps { + timelineId: string; +} + +export const TimelineKpisContainer = ({ timelineId }: KpiExpandedProps) => { + const { browserFields, indexPattern, selectedPatterns } = useSourcererDataView( + SourcererScopeName.timeline + ); + + const { uiSettings } = useKibana().services; + const esQueryConfig = useMemo(() => getEsQueryConfig(uiSettings), [uiSettings]); + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { dataProviders, filters, kqlMode } = useDeepEqualSelector((state) => + pick( + ['dataProviders', 'filters', 'kqlMode'], + getTimeline(state, timelineId) ?? timelineDefaults + ) + ); + + const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); + + const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)); + + const kqlQueryExpression = kqlQueryTimeline ?? ' '; + + const kqlQuery = useMemo( + () => ({ query: kqlQueryExpression, language: 'kuery' }), + [kqlQueryExpression] + ); + + const isActive = useMemo(() => timelineId === TimelineId.active, [timelineId]); + const getStartSelector = useMemo(() => startSelector(), []); + const getEndSelector = useMemo(() => endSelector(), []); + + const timerange: TimerangeInput = useDeepEqualSelector((state) => { + if (isActive) { + return { + from: getStartSelector(state.inputs.timeline), + to: getEndSelector(state.inputs.timeline), + interval: '', + }; + } else { + return { + from: getStartSelector(state.inputs.global), + to: getEndSelector(state.inputs.global), + interval: '', + }; + } + }); + + const combinedQueries = useMemo( + () => + combineQueries({ + config: esQueryConfig, + dataProviders, + indexPattern, + browserFields, + filters: filters ? filters : [], + kqlQuery, + kqlMode, + }), + [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQuery] + ); + + const isBlankTimeline: boolean = useMemo( + () => + (isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query)) || + combinedQueries?.filterQuery === undefined, + [dataProviders, filters, kqlQuery, combinedQueries] + ); + + const [, kpis] = useTimelineKpis({ + defaultIndex: selectedPatterns, + timerange, + isBlankTimeline, + filterQuery: combinedQueries?.filterQuery ?? '', + }); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpis.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpis.tsx new file mode 100644 index 0000000000000..9fde146b4ed4d --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpis.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import styled from 'styled-components'; + +import { EuiFlexItem, EuiFlexGroup, EuiToolTip, EuiBadge } from '@elastic/eui'; +import numeral from '@elastic/numeral'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; +import { useUiSetting$ } from '../../../../common/lib/kibana'; +import type { TimelineKpiStrategyResponse } from '../../../../../common/search_strategy'; +import { getEmptyValue } from '../../../../common/components/empty_value'; +import * as i18n from './translations'; + +export const StatsContainer = styled.span` + font-size: ${euiThemeVars.euiFontSizeXS}; + font-weight: ${euiThemeVars.euiFontWeightSemiBold}; + padding-right: 16px; + .smallDot { + width: 3px !important; + display: inline-block; + } + .euiBadge__text { + text-align: center; + width: 100%; + } +`; + +export const TimelineKPIs = React.memo(({ kpis }: { kpis: TimelineKpiStrategyResponse | null }) => { + const kpiFormat = '0,0.[000]a'; + const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); + const formattedKpis = useMemo(() => { + return { + process: kpis === null ? getEmptyValue() : numeral(kpis.processCount).format(kpiFormat), + user: kpis === null ? getEmptyValue() : numeral(kpis.userCount).format(kpiFormat), + host: kpis === null ? getEmptyValue() : numeral(kpis.hostCount).format(kpiFormat), + sourceIp: kpis === null ? getEmptyValue() : numeral(kpis.sourceIpCount).format(kpiFormat), + destinationIp: + kpis === null ? getEmptyValue() : numeral(kpis.destinationIpCount).format(kpiFormat), + }; + }, [kpis]); + + const formattedKpiToolTips = useMemo(() => { + return { + process: numeral(kpis?.processCount).format(defaultNumberFormat), + user: numeral(kpis?.userCount).format(defaultNumberFormat), + host: numeral(kpis?.hostCount).format(defaultNumberFormat), + sourceIp: numeral(kpis?.sourceIpCount).format(defaultNumberFormat), + destinationIp: numeral(kpis?.destinationIpCount).format(defaultNumberFormat), + }; + }, [kpis, defaultNumberFormat]); + + return ( + + + + {`${i18n.PROCESS_KPI_TITLE} : `} + + + {formattedKpis.process} + + + + + + + {`${i18n.USER_KPI_TITLE} : `} + + + {formattedKpis.user} + + + + + + + {`${i18n.HOST_KPI_TITLE} : `} + + + {formattedKpis.host} + + + + + + + {`${i18n.SOURCE_IP_KPI_TITLE} : `} + + + {formattedKpis.sourceIp} + + + + + + + {`${i18n.DESTINATION_IP_KPI_TITLE} : `} + + + {formattedKpis.destinationIp} + + + + + + ); +}); + +TimelineKPIs.displayName = 'TimelineKPIs'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/translations.ts new file mode 100644 index 0000000000000..177516ea4a689 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/translations.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const PROCESS_KPI_TITLE = i18n.translate( + 'xpack.securitySolution.timeline.kpis.processKpiTitle', + { + defaultMessage: 'Processes', + } +); + +export const HOST_KPI_TITLE = i18n.translate('xpack.securitySolution.timeline.kpis.hostKpiTitle', { + defaultMessage: 'Hosts', +}); + +export const SOURCE_IP_KPI_TITLE = i18n.translate( + 'xpack.securitySolution.timeline.kpis.sourceIpKpiTitle', + { + defaultMessage: 'Source IPs', + } +); + +export const DESTINATION_IP_KPI_TITLE = i18n.translate( + 'xpack.securitySolution.timeline.kpis.destinationKpiTitle', + { + defaultMessage: 'Destination IPs', + } +); + +export const USER_KPI_TITLE = i18n.translate('xpack.securitySolution.timeline.kpis.userKpiTitle', { + defaultMessage: 'Users', +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx index d43f92fc96e69..963713a2317a6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx @@ -21,6 +21,7 @@ import React, { Fragment, useCallback, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; +import type { EuiTheme } from '@kbn/react-kibana-context-styled'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { timelineActions } from '../../../store/timeline'; @@ -51,6 +52,8 @@ const FullWidthFlexGroup = styled(EuiFlexGroup)` const ScrollableFlexItem = styled(EuiFlexItem)` overflow-x: hidden; overflow-y: auto; + padding-inline: ${({ theme }) => (theme as EuiTheme).eui.euiSizeM}; + padding-block: ${({ theme }) => (theme as EuiTheme).eui.euiSizeS}; `; const VerticalRule = styled.div` @@ -202,7 +205,6 @@ const NotesTabContentComponent: React.FC = ({ timelineId } <> {createdBy && ( <> -

    {CREATED_BY}

    @@ -218,13 +220,12 @@ const NotesTabContentComponent: React.FC = ({ timelineId } ); return ( - + - +

    {NOTES}

    - { const mockGetButton = jest.fn().mockReturnValue('<>'); const props: NewTimelineProps = { - closeGearMenu: jest.fn(), + onClick: jest.fn(), timelineId: 'mockTimelineId', title: 'mockTitle', }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx index 1ac4947b5adac..0330e3ed74d57 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx @@ -27,9 +27,13 @@ NotesCountBadge.displayName = 'NotesCountBadge'; interface AddToFavoritesButtonProps { timelineId: string; + compact?: boolean; } -const AddToFavoritesButtonComponent: React.FC = ({ timelineId }) => { +const AddToFavoritesButtonComponent: React.FC = ({ + timelineId, + compact, +}) => { const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -48,7 +52,19 @@ const AddToFavoritesButtonComponent: React.FC = ({ ti [dispatch, timelineId, isFavorite] ); - return ( + const label = isFavorite ? i18n.REMOVE_FROM_FAVORITES : i18n.ADD_TO_FAVORITES; + + return compact ? ( + + ) : ( = ({ ti onClick={handleClick} data-test-subj={`timeline-favorite-${isFavorite ? 'filled' : 'empty'}-star`} disabled={disableFavoriteButton} + aria-label={label} + title={label} > - {isFavorite ? i18n.REMOVE_FROM_FAVORITES : i18n.ADD_TO_FAVORITES} + {label} ); }; @@ -66,18 +84,18 @@ AddToFavoritesButtonComponent.displayName = 'AddToFavoritesButtonComponent'; export const AddToFavoritesButton = React.memo(AddToFavoritesButtonComponent); export interface NewTimelineProps { - closeGearMenu?: () => void; + onClick?: () => void; outline?: boolean; timelineId: string; title?: string; } export const NewTimeline = React.memo( - ({ closeGearMenu, outline = false, timelineId, title = i18n.NEW_TIMELINE }) => { + ({ onClick, outline = false, timelineId, title = i18n.NEW_TIMELINE }) => { const { getButton } = useCreateTimelineButton({ timelineId, timelineType: TimelineType.default, - closeGearMenu, + onClick, }); const button = getButton({ outline, title }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx index ac69b86ec5803..10ad06c17d5bd 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx @@ -55,7 +55,7 @@ describe('NewTemplateTimeline', () => { wrapper = mount( - + ); }); @@ -92,7 +92,7 @@ describe('NewTemplateTimeline', () => { wrapper = mount( - + ); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.tsx index 4cdef0f843dfe..3d79fdbf031bd 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.tsx @@ -13,14 +13,14 @@ import { TimelineType } from '../../../../../common/api/timeline'; import { useCreateTimelineButton } from './use_create_timeline'; interface OwnProps { - closeGearMenu?: () => void; + onClick?: () => void; outline?: boolean; title?: string; timelineId?: string; } export const NewTemplateTimelineComponent: React.FC = ({ - closeGearMenu, + onClick, outline, title, timelineId = TimelineId.active, @@ -28,7 +28,7 @@ export const NewTemplateTimelineComponent: React.FC = ({ const { getButton } = useCreateTimelineButton({ timelineId, timelineType: TimelineType.template, - closeGearMenu, + onClick, }); const button = getButton({ outline, title }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx index 5fc94a2bf99c7..0693901bf2114 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx @@ -27,7 +27,7 @@ import { useDiscoverInTimelineContext } from '../../../../common/components/disc interface Props { timelineId?: string; timelineType: TimelineTypeLiteral; - closeGearMenu?: () => void; + onClick?: () => void; timeRange?: TimeRange; } @@ -35,7 +35,7 @@ interface Props { * Creates a new empty timeline at the given id. * Can be used to create new timelines or to reset timeline state. */ -export const useCreateTimeline = ({ timelineId, timelineType, closeGearMenu }: Props) => { +export const useCreateTimeline = ({ timelineId, timelineType, onClick }: Props) => { const dispatch = useDispatch(); const defaultDataViewSelector = useMemo(() => sourcererSelectors.defaultDataViewSelector(), []); const { id: dataViewId, patternList: selectedPatterns } = @@ -109,12 +109,12 @@ export const useCreateTimeline = ({ timelineId, timelineType, closeGearMenu }: P const handleCreateNewTimeline = useCallback( (options?: CreateNewTimelineOptions) => { createTimeline({ id: timelineId, show: true, timelineType, timeRange: options?.timeRange }); - if (typeof closeGearMenu === 'function') { - closeGearMenu(); + if (typeof onClick === 'function') { + onClick(); } resetDiscoverAppState(); }, - [createTimeline, timelineId, timelineType, closeGearMenu, resetDiscoverAppState] + [createTimeline, timelineId, timelineType, onClick, resetDiscoverAppState] ); return handleCreateNewTimeline; @@ -124,11 +124,11 @@ interface CreateNewTimelineOptions { timeRange?: TimeRange; } -export const useCreateTimelineButton = ({ timelineId, timelineType, closeGearMenu }: Props) => { +export const useCreateTimelineButton = ({ timelineId, timelineType, onClick }: Props) => { const handleCreateNewTimeline = useCreateTimeline({ timelineId, timelineType, - closeGearMenu, + onClick, }); const getButton = useCallback( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index 4abc4919721b6..e1e732b32e479 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -14,6 +14,7 @@ import deepEqual from 'fast-deep-equal'; import type { Filter, Query } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; import type { FilterManager, SavedQuery, SavedQueryTimeFilter } from '@kbn/data-plugin/public'; +import styled from '@emotion/styled'; import { InputsModelId } from '../../../../common/store/inputs/constants'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; @@ -47,6 +48,42 @@ export interface QueryBarTimelineComponentProps { updateReduxTime: DispatchUpdateReduxTime; } +const SearchBarContainer = styled.div` + /* + * + * hide search bar default filters as they are disturbing the layout as shown below + * + * Filters are displayed with QueryBar so below is how is the layout with default filters. + * + * + * -------------------------------- + * -----------------| |------------ + * | DataViewPicker | QueryBar | Date | + * ------------------------------------------------------------- + * | Filters | + * -------------------------------- + * + * The tree under this component makes sure that default filters are not rendered and we can separately display + * them outside query component so that layout is as below: + * + * ----------------------------------------------------------- + * | DataViewPicker | QueryBar | Date | + * ----------------------------------------------------------- + * | Filters | + * ----------------------------------------------------------- + * + * */ + .uniSearchBar .filter-items-group { + display: none; + } + + .euiDataGrid__restrictBody & { + .kbnQueryBar { + display: flex; + } + } +`; + export const TIMELINE_FILTER_DROP_AREA = 'timeline-filter-drop-area'; const getNonDropAreaFilters = (filters: Filter[] = []) => @@ -265,22 +302,24 @@ export const QueryBarTimeline = memo( ); return ( - + + + ); } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.test.tsx similarity index 83% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx rename to x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.test.tsx index 8ce39f9b535d5..44d2ee6cd6a54 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.test.tsx @@ -5,23 +5,23 @@ * 2.0. */ -import { shallow } from 'enzyme'; import React from 'react'; import { coreMock } from '@kbn/core/public/mocks'; -import { mockIndexPattern } from '../../../../common/mock'; -import { TestProviders } from '../../../../common/mock/test_providers'; +import { mockIndexPattern } from '../../../../../common/mock'; +import { TestProviders } from '../../../../../common/mock/test_providers'; import { FilterManager } from '@kbn/data-plugin/public'; -import { mockDataProviders } from '../data_providers/mock/mock_data_providers'; -import { useMountAppended } from '../../../../common/utils/use_mount_appended'; +import { mockDataProviders } from '../../data_providers/mock/mock_data_providers'; +import { useMountAppended } from '../../../../../common/utils/use_mount_appended'; -import { TimelineHeader } from '.'; -import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; +import { QueryTabHeader } from '.'; +import { TimelineStatus, TimelineType } from '../../../../../../common/api/timeline'; import { waitFor } from '@testing-library/react'; +import { TimelineId } from '../../../../../../common/types'; const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; -jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../../common/lib/kibana'); describe('Header', () => { const indexPattern = mockIndexPattern; @@ -44,21 +44,16 @@ describe('Header', () => { show: true, showCallOutUnauthorizedMsg: false, status: TimelineStatus.active, - timelineId: 'foo', + timelineId: TimelineId.test, timelineType: TimelineType.default, }; describe('rendering', () => { - test('renders correctly against snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - test('it renders the data providers when show is true', async () => { const testProps = { ...props, show: true }; const wrapper = await getWrapper( - + ); @@ -74,7 +69,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); @@ -90,7 +85,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); @@ -108,7 +103,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); @@ -129,7 +124,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); @@ -146,7 +141,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); @@ -165,7 +160,7 @@ describe('Header', () => { const wrapper = await getWrapper( - + ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx new file mode 100644 index 0000000000000..8d70c3e4d44c3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import type { FilterManager } from '@kbn/data-plugin/public'; + +import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; +import styled from '@emotion/styled'; +import { euiThemeVars } from '@kbn/ui-theme'; + +import type { TimelineStatusLiteralWithNull } from '../../../../../../common/api/timeline'; +import { TimelineStatus, TimelineType } from '../../../../../../common/api/timeline'; +import { timelineSelectors } from '../../../../store/timeline'; +import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; +import { timelineDefaults } from '../../../../store/timeline/defaults'; +import * as i18n from './translations'; +import { StatefulSearchOrFilter } from '../../search_or_filter'; +import { DataProviders } from '../../data_providers'; + +interface Props { + filterManager: FilterManager; + show: boolean; + showCallOutUnauthorizedMsg: boolean; + status: TimelineStatusLiteralWithNull; + timelineId: string; +} + +const DataProvidersContainer = styled.div<{ $shouldShowQueryBuilder: boolean }>` + position: relative; + width: 100%; + transition: 0.5s ease-in-out; + overflow: hidden; + + ${(props) => + props.$shouldShowQueryBuilder + ? `display: block; max-height: 300px; visibility: visible; margin-block-start: 0px;` + : `display: block; max-height: 0px; visibility: hidden; margin-block-start:-${euiThemeVars.euiSizeS};`} + + .${IS_DRAGGING_CLASS_NAME} & { + display: block; + max-height: 300px; + visibility: visible; + margin-block-start: 0px; + } +`; + +const QueryTabHeaderComponent: React.FC = ({ + filterManager, + show, + showCallOutUnauthorizedMsg, + status, + timelineId, +}) => { + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + + const getIsDataProviderVisible = useMemo( + () => timelineSelectors.dataProviderVisibilitySelector(), + [] + ); + + const timelineType = useDeepEqualSelector( + (state) => (getTimeline(state, timelineId) ?? timelineDefaults).timelineType + ); + + const isDataProviderVisible = useDeepEqualSelector( + (state) => getIsDataProviderVisible(state, timelineId) ?? timelineDefaults.isDataProviderVisible + ); + + const shouldShowQueryBuilder = isDataProviderVisible || timelineType === TimelineType.template; + + return ( + + + + + {showCallOutUnauthorizedMsg && ( + + + + )} + {status === TimelineStatus.immutable && ( + + + + )} + {show ? ( + + + + ) : null} + + ); +}; + +export const QueryTabHeader = React.memo(QueryTabHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts similarity index 88% rename from x-pack/plugins/security_solution/public/timelines/components/timeline/header/selectors.ts rename to x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts index 65deef3399863..02668c18b308c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../../store/timeline'; export const getTimelineSaveModalByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/translations.ts new file mode 100644 index 0000000000000..f90c46d69d230 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/translations.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CALL_OUT_UNAUTHORIZED_MSG = i18n.translate( + 'xpack.securitySolution.timeline.callOut.unauthorized.message.description', + { + defaultMessage: + 'You can use Timeline to investigate events, but you do not have the required permissions to save timelines for future use. If you need to save timelines, contact your Kibana administrator.', + } +); + +export const CALL_OUT_IMMUTABLE = i18n.translate( + 'xpack.securitySolution.timeline.callOut.immutable.message.description', + { + defaultMessage: + 'This prebuilt timeline template cannot be modified. To make changes, please duplicate this template and make modifications to the duplicate template.', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index a5d941ca6456e..390369eaf89ed 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -35,7 +35,7 @@ import { useKibana } from '../../../../common/lib/kibana'; import { defaultHeaders } from '../body/column_headers/default_headers'; import { StatefulBody } from '../body'; import { Footer, footerHeight } from '../footer'; -import { TimelineHeader } from '../header'; +import { QueryTabHeader } from './header'; import { calculateTotalPages } from '../helpers'; import { combineQueries } from '../../../../common/lib/kuery'; import { TimelineRefetch } from '../refetch_timeline'; @@ -46,7 +46,6 @@ import type { } from '../../../../../common/types/timeline'; import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { requiredFieldsForActions } from '../../../../detections/components/alerts_table/default_config'; -import { SuperDatePicker } from '../../../../common/components/super_date_picker'; import { EventDetailsWidthProvider } from '../../../../common/components/events_viewer/event_details_width_context'; import type { inputsModel, State } from '../../../../common/store'; import { inputsSelectors } from '../../../../common/store'; @@ -55,22 +54,19 @@ import { timelineDefaults } from '../../../store/timeline/defaults'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { useTimelineEventsCountPortal } from '../../../../common/hooks/use_timeline_events_count'; import type { TimelineModel } from '../../../store/timeline/model'; -import { TimelineDatePickerLock } from '../date_picker_lock'; import { useTimelineFullScreen } from '../../../../common/containers/use_full_screen'; import { activeTimeline } from '../../../containers/active_timeline_context'; import { DetailsPanel } from '../../side_panel'; import { ExitFullScreen } from '../../../../common/components/exit_full_screen'; import { getDefaultControlColumn } from '../body/control_columns'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { Sourcerer } from '../../../../common/components/sourcerer'; import { useLicense } from '../../../../common/hooks/use_license'; import { HeaderActions } from '../../../../common/components/header_actions/header_actions'; -const TimelineHeaderContainer = styled.div` - margin-top: 6px; +const QueryTabHeaderContainer = styled.div` width: 100%; `; -TimelineHeaderContainer.displayName = 'TimelineHeaderContainer'; +QueryTabHeaderContainer.displayName = 'TimelineHeaderContainer'; const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` align-items: stretch; @@ -79,8 +75,7 @@ const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` flex-direction: column; &.euiFlyoutHeader { - ${({ theme }) => - `padding: ${theme.eui.euiSizeM} ${theme.eui.euiSizeS} 0 ${theme.eui.euiSizeS};`} + ${({ theme }) => `padding: ${theme.eui.euiSizeS} 0 0 0;`} } `; @@ -320,10 +315,6 @@ export const QueryTabContentComponent: React.FC = ({ ); }, [loadingSourcerer, timelineId, isQueryLoading, dispatch]); - const isDatePickerDisabled = useMemo(() => { - return (combinedQueries && combinedQueries.kqlError != null) || false; - }, [combinedQueries]); - const leadingControlColumns = useMemo( () => getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({ @@ -333,10 +324,15 @@ export const QueryTabContentComponent: React.FC = ({ [ACTION_BUTTON_COUNT] ); + // NOTE: The timeline is blank after browser FORWARD navigation (after using back button to navigate to + // the previous page from the timeline), yet we still see total count. This is because the timeline + // is not getting refreshed when using browser navigation. + const showEventsCountBadge = !isBlankTimeline && totalCount >= 0; + return ( <> - {totalCount >= 0 ? {totalCount} : null} + {showEventsCountBadge ? {totalCount} : null} = ({ data-test-subj={`${activeTab}-tab-flyout-header`} hasBorder={false} > - + {timelineFullScreen && setTimelineFullScreen != null && ( - + + + + + )} - - - - - + + + + - - {activeTab === TimelineTabs.query && ( - - )} - + {/* TODO: This is a temporary solution to hide the KPIs until lens components play nicely with timelines */} + {/* https://github.com/elastic/kibana/issues/17156 */} + {/* */} + {/* */} + {/* */} - - - - + obj != null && typeof obj === 'object' && Object.hasOwn(obj, 'getName'); + const StatefulSearchOrFilterComponent = React.memo( ({ dataProviders, @@ -48,7 +64,59 @@ const StatefulSearchOrFilterComponent = React.memo( toStr, updateKqlMode, updateReduxTime, + timelineType, }) => { + const dispatch = useDispatch(); + + const { addError } = useAppToasts(); + + const [dataView, setDataView] = useState(); + const { + services: { data }, + } = useKibana(); + + const { indexPattern } = useSourcererDataView(SourcererScopeName.timeline); + + const getIsDataProviderVisible = useMemo( + () => timelineSelectors.dataProviderVisibilitySelector(), + [] + ); + + const isDataProviderVisible = useDeepEqualSelector((state) => + getIsDataProviderVisible(state, timelineId) + ); + + useEffect(() => { + let dv: DataView; + if (isDataView(indexPattern)) { + setDataView(indexPattern); + } else if (!filterQuery) { + const createDataView = async () => { + try { + dv = await data.dataViews.create({ title: indexPattern.title }); + setDataView(dv); + } catch (error) { + addError(error, { title: i18n.ERROR_PROCESSING_INDEX_PATTERNS }); + } + }; + createDataView(); + } + return () => { + if (dv?.id) { + data.dataViews.clearInstanceCache(dv?.id); + } + }; + }, [data.dataViews, indexPattern, filterQuery, addError]); + + const arrDataView = useMemo(() => (dataView != null ? [dataView] : []), [dataView]); + + const onFiltersUpdated = useCallback( + (newFilters: Filter[]) => { + filterManager.setFilters(newFilters); + }, + [filterManager] + ); + const setFiltersInTimeline = useCallback( (newFilters: Filter[]) => setFilters({ @@ -67,26 +135,83 @@ const StatefulSearchOrFilterComponent = React.memo( [timelineId, setSavedQueryId] ); + const toggleDataProviderVisibility = useCallback(() => { + dispatch( + setDataProviderVisibility({ id: timelineId, isDataProviderVisible: !isDataProviderVisible }) + ); + }, [isDataProviderVisible, timelineId, dispatch]); + + useEffect(() => { + /* + * If there is a change in data providers + * - data provider has some data and it was hidden, + * * it must be made visible + * + * - data provider has no data and it was visible, + * * it must be hidden + * + * */ + if (dataProviders?.length > 0) { + dispatch(setDataProviderVisibility({ id: timelineId, isDataProviderVisible: true })); + } else if (dataProviders?.length === 0) { + dispatch(setDataProviderVisibility({ id: timelineId, isDataProviderVisible: false })); + } + }, [dataProviders, dispatch, timelineId]); + return ( - + + + + + + + + + {filters && filters.length > 0 ? ( + + + + + + ) : null} + ); }, (prevProps, nextProps) => { @@ -104,7 +229,8 @@ const StatefulSearchOrFilterComponent = React.memo( deepEqual(prevProps.filterQuery, nextProps.filterQuery) && deepEqual(prevProps.kqlMode, nextProps.kqlMode) && deepEqual(prevProps.savedQueryId, nextProps.savedQueryId) && - deepEqual(prevProps.timelineId, nextProps.timelineId) + deepEqual(prevProps.timelineId, nextProps.timelineId) && + prevProps.timelineType === nextProps.timelineType ); } ); @@ -135,8 +261,10 @@ const makeMapStateToProps = () => { to: input.timerange.to, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion toStr: input.timerange.toStr!, + timelineType: timeline.timelineType, }; }; + return mapStateToProps; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index 5ddfc4cd49623..d99c38057297f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -5,40 +5,29 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import React, { useCallback } from 'react'; -import styled, { createGlobalStyle } from 'styled-components'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import styled from 'styled-components'; import type { Filter } from '@kbn/es-query'; import type { FilterManager } from '@kbn/data-plugin/public'; +import { TimelineType } from '../../../../../common/api/timeline'; +import { InputsModelId } from '../../../../common/store/inputs/constants'; import type { KqlMode } from '../../../store/timeline/model'; import type { DispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; +import { SuperDatePicker } from '../../../../common/components/super_date_picker'; import type { KueryFilterQuery } from '../../../../../common/types/timeline'; import type { DataProvider } from '../data_providers/data_provider'; import { QueryBarTimeline } from '../query_bar'; -import { EuiSuperSelect } from './super_select'; -import { options } from './helpers'; -import * as i18n from './translations'; - -const timelineSelectModeItemsClassName = 'timelineSelectModeItemsClassName'; -const searchOrFilterPopoverClassName = 'searchOrFilterPopover'; -const searchOrFilterPopoverWidth = '352px'; - -// SIDE EFFECT: the following creates a global class selector -const SearchOrFilterGlobalStyle = createGlobalStyle` - .${timelineSelectModeItemsClassName} { - width: 350px !important; - } - - .${searchOrFilterPopoverClassName}.euiPopover__panel { - width: ${searchOrFilterPopoverWidth} !important; - - .euiSuperSelect__listbox { - width: ${searchOrFilterPopoverWidth} !important; - } - } -`; +import { TimelineDatePickerLock } from '../date_picker_lock'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { Sourcerer } from '../../../../common/components/sourcerer'; +import { + DATA_PROVIDER_HIDDEN_EMPTY, + DATA_PROVIDER_HIDDEN_POPULATED, + DATA_PROVIDER_VISIBLE, +} from './translations'; interface Props { dataProviders: DataProvider[]; @@ -58,11 +47,14 @@ interface Props { to: string; toStr: string; updateReduxTime: DispatchUpdateReduxTime; + isDataProviderVisible: boolean; + toggleDataProviderVisibility: () => void; + timelineType: TimelineType; } const SearchOrFilterContainer = styled.div` - ${({ theme }) => `margin-top: ${theme.eui.euiSizeXS};`} - user-select: none; // This should not be here, it makes the entire page inaccessible + overflow-x: auto; + overflow-y: hidden; `; SearchOrFilterContainer.displayName = 'SearchOrFilterContainer'; @@ -90,33 +82,41 @@ export const SearchOrFilter = React.memo( setSavedQueryId, to, toStr, - updateKqlMode, updateReduxTime, + isDataProviderVisible, + toggleDataProviderVisibility, + timelineType, }) => { - const handleChange = useCallback( - (mode: KqlMode) => updateKqlMode({ id: timelineId, kqlMode: mode }), - [timelineId, updateKqlMode] + const isDataProviderEmpty = useMemo(() => dataProviders?.length === 0, [dataProviders]); + + const dataProviderIconTooltipContent = useMemo(() => { + if (isDataProviderVisible) { + return DATA_PROVIDER_VISIBLE; + } + if (isDataProviderEmpty) { + return DATA_PROVIDER_HIDDEN_EMPTY; + } + return DATA_PROVIDER_HIDDEN_POPULATED; + }, [isDataProviderEmpty, isDataProviderVisible]); + + const buttonColor = useMemo( + () => (isDataProviderEmpty || isDataProviderVisible ? 'primary' : 'warning'), + [isDataProviderEmpty, isDataProviderVisible] ); return ( <> - - - - - - - + + + + + ( updateReduxTime={updateReduxTime} /> + { + /* + DataProvider toggle is not needed in template timeline because + it is always visible + */ + timelineType === TimelineType.default ? ( + + + + + + ) : null + } + + + + + + + - ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts index 560a82a329684..f3577e350dc94 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts @@ -64,9 +64,30 @@ export const SEARCH_KQL_SELECTED_TEXT = i18n.translate( } ); -export const FILTER_OR_SEARCH_WITH_KQL = i18n.translate( - 'xpack.securitySolution.timeline.searchOrFilter.filterOrSearchWithKql', +export const DATA_PROVIDER_HIDDEN_POPULATED = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.dataProviderToggle.hiddenAndPopulated', { - defaultMessage: 'Filter or Search with KQL', + defaultMessage: 'Query Builder is hidden. Click here to see the existing Queries', + } +); + +export const DATA_PROVIDER_VISIBLE = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.dataProviderToggle.visible', + { + defaultMessage: 'Click here to hide Query builder', + } +); + +export const DATA_PROVIDER_HIDDEN_EMPTY = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.dataProviderToggle.hiddenAndEmpty', + { + defaultMessage: 'Click here to show the empty Query builder', + } +); + +export const ERROR_PROCESSING_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.errorProcessingDataView', + { + defaultMessage: 'Error processing Index Patterns', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index b62e36e20b938..3f65e542ed30c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -276,6 +276,10 @@ const StyledEuiTab = styled(EuiTab)` } `; +const StyledEuiTabs = styled(EuiTabs)` + padding-inline: ${(props) => props.theme.eui.euiSizeM}; +`; + const TabsContentComponent: React.FC = ({ renderCellValue, rowRenderers, @@ -389,7 +393,7 @@ const TabsContentComponent: React.FC = ({ return ( <> {!timelineFullScreen && ( - + = ({ {i18n.SECURITY_ASSISTANT} )} - + )} { @@ -18,6 +20,7 @@ jest.mock('../../common/lib/kibana', () => { http: { fetch: jest.fn(), }, + savedSearch: jest.fn(), })), }, }; @@ -448,3 +451,119 @@ describe('cleanDraftTimeline', () => { }); }); }); + +describe('copyTimeline', () => { + const mockPostTimelineResponse = { + data: { + persistTimeline: { + timeline: { + ...timelineData, + savedObjectId: '9d5693e0-a42a-11ea-b8f4-c5434162742a', + version: 'WzMzMiwxXQ==', + }, + }, + }, + }; + + const saveSavedSearchMock = jest.fn(); + const postMock = jest.fn(); + const initialSavedSearchId = 'initialId'; + const newSavedSearchId = 'newId-230820349807209752'; + + const customQuery = { + language: 'kuery', + query: '_id: *', + }; + + const dataViewMock = buildDataViewMock({ + name: 'first-data-view', + fields: shallowMockedFields, + }); + + const mockSavedSearch = { + id: initialSavedSearchId, + title: 'first title', + breakdownField: 'firstBreakdown Field', + searchSource: createSearchSourceMock({ + index: dataViewMock, + query: customQuery, + }), + }; + + beforeAll(() => { + jest.resetAllMocks(); + jest.resetModules(); + + (KibanaServices.get as jest.Mock).mockReturnValue({ + http: { + post: postMock.mockReturnValue(mockPostTimelineResponse), + }, + savedSearch: { + save: saveSavedSearchMock.mockImplementation(() => newSavedSearchId), + }, + }); + }); + + it('creates a new saved search when a saved search object is passed', async () => { + await api.copyTimeline({ + timelineId: 'test', + timeline: { + ...timelineData, + savedSearchId: 'test', + }, + savedSearch: mockSavedSearch, + }); + + // 'id' should be removed + expect(saveSavedSearchMock).toHaveBeenCalled(); + expect(saveSavedSearchMock).not.toHaveBeenCalledWith( + expect.objectContaining({ + id: initialSavedSearchId, + }) + ); + + // The new saved search id is sent to the server + expect(postMock).toHaveBeenCalledWith( + TIMELINE_COPY_URL, + expect.objectContaining({ + body: expect.stringContaining(newSavedSearchId), + }) + ); + }); + + it('applies the timeline changes before sending the POST request', async () => { + const ridiculousTimelineTitle = 'Wow, what a weirt timeline title'; + await api.copyTimeline({ + timelineId: 'test', + timeline: { + ...timelineData, + title: ridiculousTimelineTitle, + savedSearchId: 'test', + }, + savedSearch: mockSavedSearch, + }); + + // The new saved search id is sent to the server + expect(postMock).toHaveBeenCalledWith( + TIMELINE_COPY_URL, + expect.objectContaining({ + body: expect.stringContaining(ridiculousTimelineTitle), + }) + ); + }); + + it('does not save a saved search for timelines without `savedSearchId`', async () => { + jest.clearAllMocks(); + + await api.copyTimeline({ + timelineId: 'test', + timeline: { + ...timelineData, + savedSearchId: null, + }, + savedSearch: mockSavedSearch, + }); + + expect(saveSavedSearchMock).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.ts index 72a75027e417e..f39143bbfa767 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.ts @@ -11,6 +11,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { isEmpty } from 'lodash'; import { throwErrors } from '@kbn/cases-plugin/common'; +import type { SavedSearch } from '@kbn/saved-search-plugin/common'; import type { TimelineResponse, @@ -41,6 +42,7 @@ import { TIMELINE_PREPACKAGED_URL, TIMELINE_RESOLVE_URL, TIMELINES_URL, + TIMELINE_COPY_URL, TIMELINE_FAVORITE_URL, } from '../../../common/constants'; @@ -61,6 +63,7 @@ interface RequestPostTimeline { interface RequestPatchTimeline extends RequestPostTimeline { timelineId: T; version: T; + savedSearch?: SavedSearch | null; } type RequestPersistTimeline = RequestPostTimeline & Partial>; @@ -130,6 +133,7 @@ const patchTimeline = async ({ timelineId, timeline, version, + savedSearch, }: RequestPatchTimeline): Promise => { let response = null; let requestBody = null; @@ -138,6 +142,19 @@ const patchTimeline = async ({ } catch (err) { return Promise.reject(new Error(`Failed to stringify query: ${JSON.stringify(err)}`)); } + + try { + if (timeline.savedSearchId && savedSearch) { + const { savedSearch: savedSearchService } = KibanaServices.get(); + await savedSearchService.save(savedSearch, { + onTitleDuplicate: () => ({}), + copyOnSave: false, + }); + } + } catch (e) { + return Promise.reject(new Error(`Failed to copy saved search: ${timeline.savedSearchId}`)); + } + try { response = await KibanaServices.get().http.patch(TIMELINE_URL, { method: 'PATCH', @@ -153,10 +170,63 @@ const patchTimeline = async ({ return decodeTimelineResponse(response); }; +/** + * Creates a copy of the timeline with the given id. It will also apply changes to the original timeline + * which are passed as `timeline` here. + */ +export const copyTimeline = async ({ + timelineId, + timeline, + savedSearch, +}: RequestPersistTimeline): Promise => { + let response = null; + let requestBody = null; + let newSavedSearchId = null; + + try { + if (timeline.savedSearchId && savedSearch) { + const { savedSearch: savedSearchService } = KibanaServices.get(); + const savedSearchCopy = { ...savedSearch }; + // delete the id and change the title to make sure we can copy the saved search + delete savedSearchCopy.id; + newSavedSearchId = await savedSearchService.save(savedSearchCopy, { + onTitleDuplicate: () => ({}), + copyOnSave: false, + }); + } + } catch (e) { + return Promise.reject(new Error(`Failed to copy saved search: ${timeline.savedSearchId}`)); + } + + try { + requestBody = JSON.stringify({ + timeline: { ...timeline, savedSearchId: newSavedSearchId || timeline.savedSearchId }, + timelineIdToCopy: timelineId, + }); + } catch (err) { + return Promise.reject(new Error(`Failed to stringify query: ${JSON.stringify(err)}`)); + } + + try { + response = await KibanaServices.get().http.post(TIMELINE_COPY_URL, { + method: 'POST', + body: requestBody, + version: '1', + }); + } catch (err) { + // For Future developer + // We are not rejecting our promise here because we had issue with our RXJS epic + // the issue we were not able to pass the right object to it so we did manage the error in the success + return Promise.resolve(decodeTimelineErrorResponse(err.body)); + } + return decodeTimelineResponse(response); +}; + export const persistTimeline = async ({ timelineId, timeline, version, + savedSearch, }: RequestPersistTimeline): Promise => { try { if (isEmpty(timelineId) && timeline.status === TimelineStatus.draft && timeline) { @@ -187,6 +257,7 @@ export const persistTimeline = async ({ ...templateTimelineInfo, }, version: draftTimeline.data.persistTimeline.timeline.version ?? '', + savedSearch, }); } @@ -198,6 +269,7 @@ export const persistTimeline = async ({ timelineId: timelineId ?? '-1', timeline, version: version ?? '', + savedSearch, }); } catch (err) { if (err.status_code === 403 || err.body.status_code === 403) { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index bbf1a50664ac7..8440a534ad45d 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -7,6 +7,7 @@ import actionCreatorFactory from 'typescript-fsa'; import type { Filter } from '@kbn/es-query'; +import type { SavedSearch } from '@kbn/saved-search-plugin/common'; import type { SessionViewConfig } from '../../../../common/types'; import type { @@ -46,7 +47,7 @@ export const setInsertTimeline = actionCreator('SET_INSER export const addProvider = actionCreator<{ id: string; providers: DataProvider[] }>('ADD_PROVIDER'); -export const saveTimeline = actionCreator<{ id: string }>('SAVE_TIMELINE'); +export const saveTimeline = actionCreator<{ id: string; saveAsNew: boolean }>('SAVE_TIMELINE'); export const createTimeline = actionCreator('CREATE_TIMELINE'); @@ -273,4 +274,19 @@ export const setIsDiscoverSavedSearchLoaded = actionCreator<{ isDiscoverSavedSearchLoaded: boolean; }>('SET_IS_DISCOVER_SAVED_SEARCH_LOADED'); +export const initializeSavedSearch = actionCreator<{ + id: string; + savedSearch: SavedSearch; +}>('INITIALIZE_SAVED_SEARCH'); + +export const updateSavedSearch = actionCreator<{ + id: string; + savedSearch: SavedSearch; +}>('UPDATE_SAVED_SEARCH'); + +export const setDataProviderVisibility = actionCreator<{ + id: string; + isDataProviderVisible: boolean; +}>('SET_DATA_PROVIDER_VISIBLITY'); + export const setChanged = actionCreator<{ id: string; changed: boolean }>('SET_CHANGED'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 449a2aa2b13f4..1df0a997a2d8e 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -79,7 +79,9 @@ export const timelineDefaults: SubsetTimelineModel & isSelectAllChecked: false, filters: [], savedSearchId: null, + savedSearch: null, isDiscoverSavedSearchLoaded: false, + isDataProviderVisible: false, }; export const getTimelineManageDefaults = (id: string) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts index 21551dacebc66..9a273bc6ca14a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts @@ -176,6 +176,8 @@ describe('Epic Timeline', () => { id: '11169110-fc22-11e9-8ca9-072f15ce2685', savedQueryId: 'my endgame timeline query', savedSearchId: null, + savedSearch: null, + isDataProviderVisible: true, }; expect( diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index 7f1c878df692b..7c072abce73e0 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -29,13 +29,13 @@ import { takeUntil, } from 'rxjs/operators'; -import type { TimelineErrorResponse, ResponseTimeline } from '../../../../common/api/timeline'; +import type { TimelineErrorResponse, TimelineResponse } from '../../../../common/api/timeline'; import type { ColumnHeaderOptions } from '../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../common/api/timeline'; import type { inputsModel } from '../../../common/store/inputs'; import { addError } from '../../../common/store/app/actions'; -import { persistTimeline } from '../../containers/api'; +import { copyTimeline, persistTimeline } from '../../containers/api'; import { ALL_TIMELINE_QUERY_ID } from '../../containers/all'; import * as i18n from '../../pages/translations'; @@ -50,13 +50,13 @@ import { setChanged, } from './actions'; import type { TimelineModel } from './model'; -import { epicPersistNote, timelineNoteActionsType } from './epic_note'; -import { epicPersistPinnedEvent, timelinePinnedEventActionsType } from './epic_pinned_event'; -import { epicPersistTimelineFavorite, timelineFavoriteActionsType } from './epic_favorite'; +import { epicPersistNote, isNoteAction } from './epic_note'; +import { epicPersistPinnedEvent, isPinnedEventAction } from './epic_pinned_event'; +import { epicPersistTimelineFavorite, isFavoriteTimelineAction } from './epic_favorite'; import { isNotNull } from './helpers'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { myEpicTimelineId } from './my_epic_timeline_id'; -import type { ActionTimeline, TimelineEpicDependencies } from './types'; +import type { TimelineEpicDependencies } from './types'; import type { TimelineInput } from '../../../../common/search_strategy'; const isItAtimelineAction = (timelineId: string | undefined) => @@ -133,25 +133,17 @@ export const createTimelineEpic = dispatcherTimelinePersistQueue.pipe( withLatestFrom(timeline$, notes$, timelineTimeRange$), concatMap(([objAction, timeline, notes, timelineTimeRange]) => { - const action: ActionTimeline = get('action', objAction); + const action: Action = get('action', objAction); const timelineId = myEpicTimelineId.getTimelineId(); const version = myEpicTimelineId.getTimelineVersion(); const templateTimelineId = myEpicTimelineId.getTemplateTimelineId(); const templateTimelineVersion = myEpicTimelineId.getTemplateTimelineVersion(); - if (timelineNoteActionsType.has(action.type)) { - return epicPersistNote( - action, - timeline, - notes, - action$, - timeline$, - notes$, - allTimelineQuery$ - ); - } else if (timelinePinnedEventActionsType.has(action.type)) { + if (isNoteAction(action)) { + return epicPersistNote(action, notes, action$, timeline$, notes$, allTimelineQuery$); + } else if (isPinnedEventAction(action)) { return epicPersistPinnedEvent(action, timeline, action$, timeline$, allTimelineQuery$); - } else if (timelineFavoriteActionsType.has(action.type)) { + } else if (isFavoriteTimelineAction(action)) { return epicPersistTimelineFavorite( action, timeline, @@ -159,23 +151,36 @@ export const createTimelineEpic = timeline$, allTimelineQuery$ ); - } else if (action.type === saveTimeline.type) { + } else if (isSaveTimelineAction(action)) { + const saveAction = action as unknown as ReturnType; + const savedSearch = timeline[action.payload.id].savedSearch; return from( - persistTimeline({ - timelineId, - version, - timeline: { - ...convertTimelineAsInput(timeline[action.payload.id], timelineTimeRange), - templateTimelineId, - templateTimelineVersion, - }, - }) + saveAction.payload.saveAsNew && timelineId + ? copyTimeline({ + timelineId, + timeline: { + ...convertTimelineAsInput(timeline[action.payload.id], timelineTimeRange), + templateTimelineId, + templateTimelineVersion, + }, + savedSearch, + }) + : persistTimeline({ + timelineId, + version, + timeline: { + ...convertTimelineAsInput(timeline[action.payload.id], timelineTimeRange), + templateTimelineId, + templateTimelineVersion, + }, + savedSearch, + }) ).pipe( withLatestFrom(timeline$, allTimelineQuery$, kibana$), - mergeMap(([result, recentTimeline, allTimelineQuery, kibana]) => { - const error = result as TimelineErrorResponse; - if (error.status_code != null) { - switch (error.status_code) { + mergeMap(([response, recentTimeline, allTimelineQuery, kibana]) => { + if (isTimelineErrorResponse(response)) { + const error = getErrorFromResponse(response); + switch (error?.errorCode) { // conflict case 409: kibana.notifications.toasts.addDanger({ @@ -186,7 +191,7 @@ export const createTimelineEpic = default: kibana.notifications.toasts.addDanger({ title: i18n.UPDATE_TIMELINE_ERROR_TITLE, - text: error.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, + text: error?.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, }); } return [ @@ -196,9 +201,8 @@ export const createTimelineEpic = ]; } - const savedTimeline = recentTimeline[action.payload.id]; - const response: ResponseTimeline = get('data.persistTimeline', result); - if (response == null) { + const unwrappedResponse = response.data.persistTimeline; + if (unwrappedResponse == null) { kibana.notifications.toasts.addDanger({ title: i18n.UPDATE_TIMELINE_ERROR_TITLE, text: i18n.UPDATE_TIMELINE_ERROR_TEXT, @@ -209,7 +213,15 @@ export const createTimelineEpic = }), ]; } - const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + + if (unwrappedResponse.code === 403) { + return [ + showCallOutUnauthorizedMsg(), + endTimelineSaving({ + id: action.payload.id, + }), + ]; + } if (allTimelineQuery.refetch != null) { (allTimelineQuery.refetch as inputsModel.Refetch)(); @@ -219,18 +231,19 @@ export const createTimelineEpic = updateTimeline({ id: action.payload.id, timeline: { - ...savedTimeline, - updated: response.timeline.updated ?? undefined, - savedObjectId: response.timeline.savedObjectId, - version: response.timeline.version, - status: response.timeline.status ?? TimelineStatus.active, - timelineType: response.timeline.timelineType ?? TimelineType.default, - templateTimelineId: response.timeline.templateTimelineId ?? null, - templateTimelineVersion: response.timeline.templateTimelineVersion ?? null, + ...recentTimeline[action.payload.id], + updated: unwrappedResponse.timeline.updated ?? undefined, + savedObjectId: unwrappedResponse.timeline.savedObjectId, + version: unwrappedResponse.timeline.version, + status: unwrappedResponse.timeline.status ?? TimelineStatus.active, + timelineType: unwrappedResponse.timeline.timelineType ?? TimelineType.default, + templateTimelineId: unwrappedResponse.timeline.templateTimelineId ?? null, + templateTimelineVersion: + unwrappedResponse.timeline.templateTimelineVersion ?? null, + savedSearchId: unwrappedResponse.timeline.savedSearchId ?? null, isSaving: false, }, }), - ...callOutMsg, setChanged({ id: action.payload.id, changed: false, @@ -238,7 +251,7 @@ export const createTimelineEpic = endTimelineSaving({ id: action.payload.id, }), - ].filter(Boolean); + ]; }), startWith(startTimelineSaving({ id: action.payload.id })), takeUntil( @@ -275,6 +288,10 @@ export const createTimelineEpic = ); }; +function isSaveTimelineAction(action: Action): action is ReturnType { + return action.type === saveTimeline.type; +} + const timelineInput: TimelineInput = { columns: null, dataProviders: null, @@ -389,3 +406,17 @@ const convertToString = (obj: unknown) => { return ''; } }; + +type PossibleResponse = TimelineResponse | TimelineErrorResponse; + +function isTimelineErrorResponse(response: PossibleResponse): response is TimelineErrorResponse { + return 'status_code' in response || 'statusCode' in response; +} + +function getErrorFromResponse(response: TimelineErrorResponse) { + if ('status_code' in response) { + return { errorCode: response.status_code, message: response.message }; + } else if ('statusCode' in response) { + return { errorCode: response.statusCode, message: response.message }; + } +} diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts index 430f7bc71aa54..ed4294207b308 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts @@ -32,6 +32,7 @@ import { setFilters, setSavedQueryId, setChanged, + updateSavedSearch, } from './actions'; /** @@ -59,6 +60,8 @@ const timelineChangedTypes = new Set([ updateSort.type, updateRange.type, upsertColumn.type, + + updateSavedSearch.type, ]); /** diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts index 990bb229761c0..ff501fb4761de 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts @@ -22,16 +22,22 @@ import { } from './actions'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { myEpicTimelineId } from './my_epic_timeline_id'; -import type { ActionTimeline, TimelineById } from './types'; +import type { TimelineById } from './types'; import type { inputsModel } from '../../../common/store/inputs'; import type { ResponseFavoriteTimeline } from '../../../../common/api/timeline'; import { TimelineType } from '../../../../common/api/timeline'; import { persistFavorite } from '../../containers/api'; -export const timelineFavoriteActionsType = new Set([updateIsFavorite.type]); +type FavoriteTimelineAction = ReturnType; + +const timelineFavoriteActionsType = new Set([updateIsFavorite.type]); + +export function isFavoriteTimelineAction(action: Action): action is FavoriteTimelineAction { + return timelineFavoriteActionsType.has(action.type); +} export const epicPersistTimelineFavorite = ( - action: ActionTimeline, + action: FavoriteTimelineAction, timeline: TimelineById, action$: Observable, timeline$: Observable, @@ -108,7 +114,7 @@ export const createTimelineFavoriteEpic = (): Epic => (action$) => { return action$.pipe( - filter((action) => timelineFavoriteActionsType.has(action.type)), + filter(isFavoriteTimelineAction), mergeMap((action) => { dispatcherTimelinePersistQueue.next({ action }); return EMPTY; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts index a9992d69c9260..01e612302ca31 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts @@ -26,15 +26,20 @@ import { } from './actions'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; -import type { ActionTimeline, TimelineById } from './types'; +import type { TimelineById } from './types'; import { persistNote } from '../../containers/notes/api'; import type { ResponseNote } from '../../../../common/api/timeline'; -export const timelineNoteActionsType = new Set([addNote.type, addNoteToEvent.type]); +type NoteAction = ReturnType; + +const timelineNoteActionsType = new Set([addNote.type, addNoteToEvent.type]); + +export function isNoteAction(action: Action): action is NoteAction { + return timelineNoteActionsType.has(action.type); +} export const epicPersistNote = ( - action: ActionTimeline, - timeline: TimelineById, + action: NoteAction, notes: NotesById, action$: Observable, timeline$: Observable, @@ -47,7 +52,7 @@ export const epicPersistNote = ( noteId: null, version: null, note: { - eventId: action.payload.eventId, + eventId: 'eventId' in action.payload ? action.payload.eventId : undefined, note: getNote(action.payload.noteId, notes), timelineId: myEpicTimelineId.getTimelineId(), }, @@ -125,7 +130,7 @@ export const createTimelineNoteEpic = (): Epic => (action$) => action$.pipe( - filter((action) => timelineNoteActionsType.has(action.type)), + filter(isNoteAction), switchMap((action) => { dispatcherTimelinePersistQueue.next({ action }); return EMPTY; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts index 9231eefb46195..b99de117e785e 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts @@ -25,13 +25,19 @@ import { } from './actions'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; -import type { ActionTimeline, TimelineById } from './types'; +import type { TimelineById } from './types'; import { persistPinnedEvent } from '../../containers/pinned_event/api'; -export const timelinePinnedEventActionsType = new Set([pinEvent.type, unPinEvent.type]); +type PinnedEventAction = ReturnType; + +const timelinePinnedEventActionsType = new Set([pinEvent.type, unPinEvent.type]); + +export function isPinnedEventAction(action: Action): action is PinnedEventAction { + return timelinePinnedEventActionsType.has(action.type); +} export const epicPersistPinnedEvent = ( - action: ActionTimeline, + action: PinnedEventAction, timeline: TimelineById, action$: Observable, timeline$: Observable, @@ -129,7 +135,7 @@ export const createTimelinePinnedEventEpic = (): Epic => (action$) => action$.pipe( - filter((action) => timelinePinnedEventActionsType.has(action.type)), + filter(isPinnedEventAction), mergeMap((action) => { dispatcherTimelinePersistQueue.next({ action }); return EMPTY; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts similarity index 99% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts rename to x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts index 980573f0c73c3..e0ca1b6dc681d 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts @@ -138,6 +138,8 @@ const basicTimeline: TimelineModel = { title: '', version: null, savedSearchId: null, + savedSearch: null, + isDataProviderVisible: true, }; const timelineByIdMock: TimelineById = { foo: { ...basicTimeline }, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index 81a19dde1cea9..47e15be86e2a1 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -7,6 +7,7 @@ import type { FilterManager } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; +import type { SavedSearch } from '@kbn/saved-search-plugin/common'; import type { ExpandedDetailTimeline, SessionViewConfig } from '../../../../common/types'; import type { EqlOptionsSelected, @@ -135,7 +136,10 @@ export interface TimelineModel { selectAll: boolean; /* discover saved search Id */ savedSearchId: string | null; + /* local saved search object, it's not sent to the server */ + savedSearch: SavedSearch | null; isDiscoverSavedSearchLoaded?: boolean; + isDataProviderVisible: boolean; /** used to mark the timeline as unsaved in the UI */ changed?: boolean; } @@ -192,7 +196,9 @@ export type SubsetTimelineModel = Readonly< | 'filters' | 'filterManager' | 'savedSearchId' + | 'savedSearch' | 'isDiscoverSavedSearchLoaded' + | 'isDataProviderVisible' | 'changed' > >; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 7da0d41fe8955..6947b207874a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -58,7 +58,10 @@ import { clearEventsDeleted, clearEventsLoading, updateSavedSearchId, + updateSavedSearch, + initializeSavedSearch, setIsDiscoverSavedSearchLoaded, + setDataProviderVisibility, setChanged, } from './actions'; @@ -520,6 +523,26 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) }, }, })) + .case(initializeSavedSearch, (state, { id, savedSearch }) => ({ + ...state, + timelineById: { + ...state.timelineById, + [id]: { + ...state.timelineById[id], + savedSearch, + }, + }, + })) + .case(updateSavedSearch, (state, { id, savedSearch }) => ({ + ...state, + timelineById: { + ...state.timelineById, + [id]: { + ...state.timelineById[id], + savedSearch, + }, + }, + })) .case(setIsDiscoverSavedSearchLoaded, (state, { id, isDiscoverSavedSearchLoaded }) => ({ ...state, timelineById: { @@ -530,6 +553,18 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) }, }, })) + .case(setDataProviderVisibility, (state, { id, isDataProviderVisible }) => { + return { + ...state, + timelineById: { + ...state.timelineById, + [id]: { + ...state.timelineById[id], + isDataProviderVisible, + }, + }, + }; + }) .case(setChanged, (state, { id, changed }) => ({ ...state, timelineById: { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts index a62af5f4f5897..d4078373aaff1 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts @@ -54,3 +54,6 @@ export const getKqlFilterKuerySelector = () => ? timeline.kqlQuery.filterQuery.kuery : null ); + +export const dataProviderVisibilitySelector = () => + createSelector(selectTimeline, (timeline) => timeline.isDataProviderVisible); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts index 365196a228444..cd55fb83335d2 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { Action } from 'redux'; import type { Observable } from 'rxjs'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; @@ -40,14 +39,6 @@ export interface TimelineState { insertTimeline: InsertTimeline | null; } -export interface ActionTimeline extends Action { - payload: { - id: string; - eventId: string; - noteId: string; - }; -} - export interface TimelineEpicDependencies { timelineByIdSelector: (state: State) => TimelineById; timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 0dcd00d3db78b..16a75ce105262 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -81,7 +81,7 @@ import type { BreadcrumbsNav } from './common/breadcrumbs/types'; import type { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service'; import type { ExperimentalFeatures } from '../common/experimental_features'; import type { DeepLinksFormatter } from './common/links/deep_links'; -import type { SetComponents, GetComponent$ } from './contract_components'; +import type { SetComponents, GetComponents$ } from './contract_components'; import type { ConfigSettings } from '../common/config_settings'; export interface SetupPlugins { @@ -147,7 +147,7 @@ export interface StartPluginsDependencies extends StartPlugins { export interface ContractStartServices { extraRoutes$: Observable; - getComponent$: GetComponent$; + getComponents$: GetComponents$; upselling: UpsellingService; } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader.js b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader.js new file mode 100644 index 0000000000000..050be8e0cf071 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader.js @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +require('../../../../../src/setup_node_env'); +require('./agent_downloader_cli').cli(); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts new file mode 100644 index 0000000000000..ab1da6a3f208f --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/agent_downloader.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ok } from 'assert'; +import type { RunFn } from '@kbn/dev-cli-runner'; +import type { ToolingLog } from '@kbn/tooling-log'; +import { getAgentDownloadUrl, getAgentFileName } from '../common/fleet_services'; +import { downloadAndStoreAgent } from '../common/agent_downloads_service'; + +const downloadAndStoreElasticAgent = async ( + version: string, + closestMatch: boolean, + log: ToolingLog +) => { + const downloadUrlResponse = await getAgentDownloadUrl(version, closestMatch, log); + const fileNameNoExtension = getAgentFileName(version); + const agentFile = `${fileNameNoExtension}.tar.gz`; + await downloadAndStoreAgent(downloadUrlResponse.url, agentFile); +}; + +export const agentDownloaderRunner: RunFn = async (cliContext) => { + ok(cliContext.flags.version, 'version argument is required'); + await downloadAndStoreElasticAgent( + cliContext.flags.version as string, + cliContext.flags.closestMatch as boolean, + cliContext.log + ); +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/index.ts new file mode 100644 index 0000000000000..7cc6988b63de8 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_downloader_cli/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { run } from '@kbn/dev-cli-runner'; +import { agentDownloaderRunner } from './agent_downloader'; + +export const cli = () => { + run( + agentDownloaderRunner, + + // Options + { + description: `Elastic Agent downloader`, + flags: { + string: ['version'], + boolean: ['closestMatch'], + default: { + closestMatch: true, + }, + help: ` + --version Required. Elastic agent version to be downloaded. + --closestMatch Optional. Use closest elastic agent version to match with. + `, + }, + } + ); +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts index 34f473a854460..410ddd65cf842 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts @@ -64,8 +64,10 @@ class AgentDownloadStorage extends SettingsStorage } } - public getPathsForUrl(agentDownloadUrl: string): DownloadedAgentInfo { - const filename = agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#'); + public getPathsForUrl(agentDownloadUrl: string, agentFileName?: string): DownloadedAgentInfo { + const filename = agentFileName + ? agentFileName + : agentDownloadUrl.replace(/^https?:\/\//gi, '').replace(/\//g, '#'); const directory = this.downloadsDirFullPath; const fullFilePath = this.buildPath(join(this.downloadsDirName, filename)); @@ -76,14 +78,17 @@ class AgentDownloadStorage extends SettingsStorage }; } - public async downloadAndStore(agentDownloadUrl: string): Promise { + public async downloadAndStore( + agentDownloadUrl: string, + agentFileName?: string + ): Promise { this.log.debug(`Downloading and storing: ${agentDownloadUrl}`); // TODO: should we add "retry" attempts to file downloads? await this.ensureExists(); - const newDownloadInfo = this.getPathsForUrl(agentDownloadUrl); + const newDownloadInfo = this.getPathsForUrl(agentDownloadUrl, agentFileName); // If download is already present on disk, then just return that info. No need to re-download it if (fs.existsSync(newDownloadInfo.fullFilePath)) { @@ -154,6 +159,18 @@ class AgentDownloadStorage extends SettingsStorage return response; } + + public isAgentDownloadFromDiskAvailable(filename: string): DownloadedAgentInfo | undefined { + if (fs.existsSync(join(this.downloadsDirFullPath, filename))) { + return { + filename, + /** The local directory where downloads are stored */ + directory: this.downloadsDirFullPath, + /** The full local file path and name */ + fullFilePath: join(this.downloadsDirFullPath, filename), + }; + } + } } const handleProcessInterruptions = async ( @@ -203,11 +220,16 @@ export interface DownloadAndStoreAgentResponse extends DownloadedAgentInfo { * already exists on disk, then no download is actually done - the information about the cached * version is returned instead * @param agentDownloadUrl + * @param agentFileName */ export const downloadAndStoreAgent = async ( - agentDownloadUrl: string + agentDownloadUrl: string, + agentFileName?: string ): Promise => { - const downloadedAgent = await agentDownloadsClient.downloadAndStore(agentDownloadUrl); + const downloadedAgent = await agentDownloadsClient.downloadAndStore( + agentDownloadUrl, + agentFileName + ); return { url: agentDownloadUrl, @@ -221,3 +243,9 @@ export const downloadAndStoreAgent = async ( export const cleanupDownloads = async (): ReturnType => { return agentDownloadsClient.cleanupDownloads(); }; + +export const isAgentDownloadFromDiskAvailable = ( + fileName: string +): DownloadedAgentInfo | undefined => { + return agentDownloadsClient.isAgentDownloadFromDiskAvailable(fileName); +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index 26ca9d6474393..6477bfc3bebeb 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -373,6 +373,16 @@ export const getAgentVersionMatchingCurrentStack = async ( return version; }; +// Generates a file name using system arch and an agent version. +export const getAgentFileName = (agentVersion: string): string => { + const downloadArch = + { arm64: 'arm64', x64: 'x86_64' }[process.arch as string] ?? + `UNSUPPORTED_ARCHITECTURE_${process.arch}`; + const fileName = `elastic-agent-${agentVersion}-linux-${downloadArch}`; + + return fileName; +}; + interface ElasticArtifactSearchResponse { manifest: { 'last-update-time': string; @@ -414,11 +424,9 @@ export const getAgentDownloadUrl = async ( log?: ToolingLog ): Promise => { const agentVersion = closestMatch ? await getLatestAgentDownloadVersion(version, log) : version; - const downloadArch = - { arm64: 'arm64', x64: 'x86_64' }[process.arch as string] ?? - `UNSUPPORTED_ARCHITECTURE_${process.arch}`; - const fileNameNoExtension = `elastic-agent-${agentVersion}-linux-${downloadArch}`; - const agentFile = `${fileNameNoExtension}.tar.gz`; + + const fileNameWithoutExtension = getAgentFileName(agentVersion); + const agentFile = `${fileNameWithoutExtension}.tar.gz`; const artifactSearchUrl = `https://artifacts-api.elastic.co/v1/search/${agentVersion}/${agentFile}`; log?.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`); @@ -444,7 +452,7 @@ export const getAgentDownloadUrl = async ( return { url: searchResult.packages[agentFile].url, fileName: agentFile, - dirName: fileNameNoExtension, + dirName: fileNameWithoutExtension, }; }; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile b/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile index fd33efaeab625..b058d17ad0764 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile @@ -5,6 +5,7 @@ hostname = ENV["VMNAME"] || 'ubuntu' cachedAgentSource = ENV["CACHED_AGENT_SOURCE"] || '' cachedAgentFilename = ENV["CACHED_AGENT_FILENAME"] || '' +agentDestinationFolder = ENV["AGENT_DESTINATION_FOLDER"] || '' Vagrant.configure("2") do |config| config.vm.hostname = hostname @@ -29,6 +30,7 @@ Vagrant.configure("2") do |config| end config.vm.provision "file", source: cachedAgentSource, destination: "~/#{cachedAgentFilename}" - config.vm.provision "shell", inline: "tar -zxf #{cachedAgentFilename} && rm -f #{cachedAgentFilename}" + config.vm.provision "shell", inline: "mkdir #{agentDestinationFolder}" + config.vm.provision "shell", inline: "tar -zxf #{cachedAgentFilename} --directory #{agentDestinationFolder} --strip-components=1 && rm -f #{cachedAgentFilename}" config.vm.provision "shell", inline: "sudo apt-get install unzip" end diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts index a0efd908f80d7..17c74b1bf6fc3 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts @@ -249,6 +249,7 @@ const createVagrantVm = async ({ VMNAME: name, CACHED_AGENT_SOURCE: agentFullFilePath, CACHED_AGENT_FILENAME: agentFileName, + AGENT_DESTINATION_FOLDER: agentFileName.replace('.tar.gz', ''), }, // Only `pipe` STDERR to parent process stdio: ['inherit', 'inherit', 'pipe'], diff --git a/x-pack/plugins/security_solution/scripts/openapi/bundle.js b/x-pack/plugins/security_solution/scripts/openapi/bundle.js new file mode 100644 index 0000000000000..6cfa1507ea9ee --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/openapi/bundle.js @@ -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. + */ + +require('../../../../../src/setup_node_env'); +const { bundle } = require('@kbn/openapi-bundler'); +const { resolve } = require('path'); + +const SECURITY_SOLUTION_ROOT = resolve(__dirname, '../..'); + +bundle({ + rootDir: SECURITY_SOLUTION_ROOT, + sourceGlob: './common/api/**/*.schema.yaml', + outputFilePath: './target/openapi/security_solution.bundled.schema.yaml', +}); diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/get_ftr_config.ts b/x-pack/plugins/security_solution/scripts/run_cypress/get_ftr_config.ts index cc3972cba0b2f..de5cb675a42e1 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/get_ftr_config.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/get_ftr_config.ts @@ -171,17 +171,6 @@ export const getFTRConfig = ({ } } - // Serverless Specific - if (vars.serverless) { - log.info(`Serverless mode detected`); - - vars.esTestCluster.serverArgs.push( - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://host.docker.internal:${kibanaPort}`, - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://host.docker.internal:${kibanaPort}/logout`, - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://host.docker.internal:${kibanaPort}/api/security/saml/callback` - ); - } - if (specFileFTRConfig?.productTypes) { if (vars.serverless) { vars.kbnTestServer.serverArgs.push( diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts new file mode 100644 index 0000000000000..906402b877e0e --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { CasesClient } from '@kbn/cases-plugin/server'; +import type { Logger } from '@kbn/logging'; +import type { EndpointAppContext } from '../../types'; +import type { ResponseActionsProvider } from './types'; +import type { + ActionDetails, + GetProcessesActionOutputContent, + KillOrSuspendProcessRequestBody, + KillProcessActionOutputContent, + ResponseActionExecuteOutputContent, + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters, + ResponseActionParametersWithPidOrEntityId, + ResponseActionsExecuteParameters, + ResponseActionUploadOutputContent, + ResponseActionUploadParameters, + SuspendProcessActionOutputContent, +} from '../../../../common/endpoint/types'; +import type { + IsolationRouteRequestBody, + ExecuteActionRequestBody, + GetProcessesRequestBody, + ResponseActionGetFileRequestBody, + UploadActionApiRequestBody, +} from '../../../../common/api/endpoint'; + +export interface BaseActionsProviderOptions { + endpointContext: EndpointAppContext; + esClient: ElasticsearchClient; + casesClient?: CasesClient; + /** Username that will be stored along with the action's ES documents */ + username: string; +} + +export abstract class BaseResponseActionsClient implements ResponseActionsProvider { + protected readonly log: Logger; + + constructor(protected readonly options: BaseActionsProviderOptions) { + this.log = options.endpointContext.logFactory.get(this.constructor.name ?? 'ActionsProvider'); + } + + // TODO:PT implement a generic way to update cases without relying on the Attachments being endpoint agents + // protected async updateCases(): Promise { + // throw new Error('Method not yet implemented'); + // } + + public abstract isolate(options: IsolationRouteRequestBody): Promise; + + public abstract release(options: IsolationRouteRequestBody): Promise; + + public abstract killProcess( + options: KillOrSuspendProcessRequestBody + ): Promise< + ActionDetails + >; + + public abstract suspendProcess( + options: KillOrSuspendProcessRequestBody + ): Promise< + ActionDetails + >; + + public abstract runningProcesses( + options: GetProcessesRequestBody + ): Promise>; + + public abstract getFile( + options: ResponseActionGetFileRequestBody + ): Promise>; + + public abstract execute( + options: ExecuteActionRequestBody + ): Promise>; + + public abstract upload( + options: UploadActionApiRequestBody + ): Promise>; +} diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts new file mode 100644 index 0000000000000..5578128cf4248 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + ActionDetails, + KillOrSuspendProcessRequestBody, + KillProcessActionOutputContent, + ResponseActionParametersWithPidOrEntityId, + SuspendProcessActionOutputContent, + GetProcessesActionOutputContent, + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters, + ResponseActionExecuteOutputContent, + ResponseActionsExecuteParameters, + ResponseActionUploadOutputContent, + ResponseActionUploadParameters, +} from '../../../../common/endpoint/types'; +import type { + IsolationRouteRequestBody, + GetProcessesRequestBody, + ResponseActionGetFileRequestBody, + ExecuteActionRequestBody, + UploadActionApiRequestBody, +} from '../../../../common/api/endpoint'; + +/** + * The interface required for a Response Actions provider + */ +export interface ResponseActionsProvider { + isolate: (options: IsolationRouteRequestBody) => Promise; + + release: (options: IsolationRouteRequestBody) => Promise; + + killProcess: ( + options: KillOrSuspendProcessRequestBody + ) => Promise< + ActionDetails + >; + + suspendProcess: ( + options: KillOrSuspendProcessRequestBody + ) => Promise< + ActionDetails + >; + + runningProcesses: ( + options: GetProcessesRequestBody + ) => Promise>; + + getFile: ( + options: ResponseActionGetFileRequestBody + ) => Promise>; + + execute: ( + options: ExecuteActionRequestBody + ) => Promise>; + + upload: ( + options: UploadActionApiRequestBody + ) => Promise>; +} diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.test.ts deleted file mode 100644 index d8cda7554a089..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.test.ts +++ /dev/null @@ -1,201 +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 { HttpApiTestSetupMock } from '../../mocks'; -import { createHttpApiTestSetupMock } from '../../mocks'; -import type { UploadActionApiRequestBody } from '../../../../common/api/endpoint'; -import type { getActionFileUploadHandler } from './file_upload_handler'; -import { registerActionFileUploadRoute } from './file_upload_handler'; -import { UPLOAD_ROUTE } from '../../../../common/endpoint/constants'; -import { getEndpointAuthzInitialStateMock } from '../../../../common/endpoint/service/authz/mocks'; -import { EndpointAuthorizationError } from '../../errors'; -import type { HapiReadableStream } from '../../../types'; -import { createHapiReadableStreamMock } from '../../services/actions/mocks'; -import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; -import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; -import type { ActionDetails } from '../../../../common/endpoint/types'; -import { omit } from 'lodash'; -import type { FleetToHostFileClientInterface } from '@kbn/fleet-plugin/server'; - -describe('Upload response action create API handler', () => { - type UploadHttpApiTestSetupMock = HttpApiTestSetupMock; - - let testSetup: UploadHttpApiTestSetupMock; - let httpRequestMock: ReturnType; - let httpHandlerContextMock: UploadHttpApiTestSetupMock['httpHandlerContextMock']; - let httpResponseMock: UploadHttpApiTestSetupMock['httpResponseMock']; - - let fleetFilesClientMock: jest.Mocked; - - beforeEach(async () => { - testSetup = createHttpApiTestSetupMock(); - - ({ httpHandlerContextMock, httpResponseMock } = testSetup); - httpRequestMock = testSetup.createRequestMock(); - - fleetFilesClientMock = - (await testSetup.endpointAppContextMock.service.getFleetToHostFilesClient()) as jest.Mocked; - }); - - describe('registerActionFileUploadRoute()', () => { - it('should register the route', () => { - registerActionFileUploadRoute(testSetup.routerMock, testSetup.endpointAppContextMock); - - expect( - testSetup.getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31') - ).toBeDefined(); - }); - - it('should NOT register route if feature flag is false', () => { - // @ts-expect-error - testSetup.endpointAppContextMock.experimentalFeatures.responseActionUploadEnabled = false; - registerActionFileUploadRoute(testSetup.routerMock, testSetup.endpointAppContextMock); - - expect(() => - testSetup.getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31') - ).toThrow('No routes registered for [POST /api/endpoint/action/upload]'); - }); - - it('should use maxUploadResponseActionFileBytes config value', () => { - // @ts-expect-error - testSetup.endpointAppContextMock.serverConfig.maxUploadResponseActionFileBytes = 999; - registerActionFileUploadRoute(testSetup.routerMock, testSetup.endpointAppContextMock); - - expect( - testSetup.getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31').routeConfig - ?.options?.body - ).toEqual({ - accepts: ['multipart/form-data'], - maxBytes: 999, - output: 'stream', - }); - }); - - it('should error if user has no authz to api', async () => { - ( - (await httpHandlerContextMock.securitySolution).getEndpointAuthz as jest.Mock - ).mockResolvedValue(getEndpointAuthzInitialStateMock({ canWriteFileOperations: false })); - registerActionFileUploadRoute(testSetup.routerMock, testSetup.endpointAppContextMock); - await testSetup - .getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31') - .routeHandler(httpHandlerContextMock, httpRequestMock, httpResponseMock); - - expect(httpResponseMock.forbidden).toHaveBeenCalledWith({ - body: expect.any(EndpointAuthorizationError), - }); - }); - }); - - describe('route request handler', () => { - let callHandler: () => ReturnType>; - let fileContent: HapiReadableStream; - let createdUploadAction: ActionDetails; - - beforeEach(() => { - fileContent = createHapiReadableStreamMock(); - - const reqBody: UploadActionApiRequestBody = { - file: fileContent, - endpoint_ids: ['123-456'], - parameters: { - overwrite: true, - }, - }; - - httpRequestMock = testSetup.createRequestMock({ body: reqBody }); - registerActionFileUploadRoute(testSetup.routerMock, testSetup.endpointAppContextMock); - - createdUploadAction = new EndpointActionGenerator('seed').generateActionDetails({ - command: 'upload', - }); - - ( - testSetup.endpointAppContextMock.service.getActionCreateService().createAction as jest.Mock - ).mockResolvedValue(createdUploadAction); - - (testSetup.endpointAppContextMock.service.getEndpointMetadataService as jest.Mock) = jest - .fn() - .mockReturnValue({ - getMetadataForEndpoints: jest.fn().mockResolvedValue([ - { - elastic: { - agent: { - id: '123-456', - }, - }, - }, - ]), - }); - - const handler: ReturnType = - testSetup.getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31').routeHandler; - - callHandler = () => handler(httpHandlerContextMock, httpRequestMock, httpResponseMock); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a file', async () => { - await callHandler(); - - expect(fleetFilesClientMock.create).toHaveBeenCalledWith(fileContent, ['123-456']); - }); - - it('should create the action using parameters with stored file info', async () => { - await callHandler(); - - const createActionMock = testSetup.endpointAppContextMock.service.getActionCreateService() - .createAction as jest.Mock; - - expect(createActionMock).toHaveBeenCalledWith( - { - command: 'upload', - endpoint_ids: ['123-456'], - parameters: { - file_id: '123-456-789', - file_name: 'foo.txt', - file_sha256: '96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3', - file_size: 45632, - overwrite: true, - }, - user: undefined, - }, - ['123-456'] - ); - }); - - it('should delete file if creation of Action fails', async () => { - const createActionMock = testSetup.endpointAppContextMock.service.getActionCreateService() - .createAction as jest.Mock; - createActionMock.mockImplementation(async () => { - throw new CustomHttpRequestError('oh oh'); - }); - await callHandler(); - - expect(fleetFilesClientMock.delete).toHaveBeenCalledWith('123-456-789'); - }); - - it('should update file with action id', async () => { - await callHandler(); - - expect(fleetFilesClientMock.update).toHaveBeenCalledWith('123-456-789', { actionId: '123' }); - }); - - it('should return expected response on success', async () => { - await callHandler(); - - expect(httpResponseMock.ok).toHaveBeenCalledWith({ - body: { - action: createdUploadAction.action, - data: omit(createdUploadAction, 'action'), - }, - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.ts deleted file mode 100644 index 3b51bb48a0104..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_upload_handler.ts +++ /dev/null @@ -1,158 +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 { RequestHandler } from '@kbn/core/server'; -import type { UploadActionApiRequestBody } from '../../../../common/api/endpoint'; -import { UploadActionRequestSchema } from '../../../../common/api/endpoint'; -import type { ResponseActionsApiCommandNames } from '../../../../common/endpoint/service/response_actions/constants'; -import type { - ResponseActionUploadParameters, - ResponseActionUploadOutputContent, - HostMetadata, -} from '../../../../common/endpoint/types'; -import { UPLOAD_ROUTE } from '../../../../common/endpoint/constants'; -import { withEndpointAuthz } from '../with_endpoint_authz'; -import type { - SecuritySolutionPluginRouter, - SecuritySolutionRequestHandlerContext, - HapiReadableStream, -} from '../../../types'; -import type { EndpointAppContext } from '../../types'; -import { errorHandler } from '../error_handler'; -import { updateCases } from '../../services/actions/create/update_cases'; - -export const registerActionFileUploadRoute = ( - router: SecuritySolutionPluginRouter, - endpointContext: EndpointAppContext -) => { - if (!endpointContext.experimentalFeatures.responseActionUploadEnabled) { - return; - } - - const logger = endpointContext.logFactory.get('uploadAction'); - - router.versioned - .post({ - access: 'public', - path: UPLOAD_ROUTE, - options: { - authRequired: true, - tags: ['access:securitySolution'], - body: { - accepts: ['multipart/form-data'], - output: 'stream', - maxBytes: endpointContext.serverConfig.maxUploadResponseActionFileBytes, - }, - }, - }) - .addVersion( - { - version: '2023-10-31', - validate: { - request: UploadActionRequestSchema, - }, - }, - withEndpointAuthz( - { all: ['canWriteFileOperations'] }, - logger, - getActionFileUploadHandler(endpointContext) - ) - ); -}; - -export const getActionFileUploadHandler = ( - endpointContext: EndpointAppContext -): RequestHandler< - never, - never, - UploadActionApiRequestBody, - SecuritySolutionRequestHandlerContext -> => { - const logger = endpointContext.logFactory.get('uploadAction'); - - return async (context, req, res) => { - const fleetFiles = await endpointContext.service.getFleetToHostFilesClient(); - const user = endpointContext.service.security?.authc.getCurrentUser(req); - const fileStream = req.body.file as HapiReadableStream; - const { file: _, parameters: userParams, ...actionPayload } = req.body; - const uploadParameters: ResponseActionUploadParameters = { - ...userParams, - file_id: '', - file_name: '', - file_sha256: '', - file_size: 0, - }; - - try { - const createdFile = await fleetFiles.create(fileStream, actionPayload.endpoint_ids); - - uploadParameters.file_id = createdFile.id; - uploadParameters.file_name = createdFile.name; - uploadParameters.file_sha256 = createdFile.sha256; - uploadParameters.file_size = createdFile.size; - } catch (err) { - return errorHandler(logger, res, err); - } - - const createActionPayload = { - ...actionPayload, - parameters: uploadParameters, - command: 'upload' as ResponseActionsApiCommandNames, - user, - }; - - const esClient = (await context.core).elasticsearch.client.asInternalUser; - const endpointData = await endpointContext.service - .getEndpointMetadataService() - .getMetadataForEndpoints(esClient, [...new Set(createActionPayload.endpoint_ids)]); - const agentIds = endpointData.map((endpoint: HostMetadata) => endpoint.elastic.agent.id); - - try { - const casesClient = await endpointContext.service.getCasesClient(req); - const { action: actionId, ...data } = await endpointContext.service - .getActionCreateService() - .createAction( - createActionPayload, - agentIds - ); - - // Update the file meta to include the action id, and if any errors (unlikely), - // then just log them and still allow api to return success since the action has - // already been created and potentially dispatched to Endpoint. Action ID is not - // needed by the Endpoint or fleet-server's API, so no need to fail here - try { - await fleetFiles.update(uploadParameters.file_id, { actionId: data.id }); - } catch (e) { - logger.warn(`Attempt to update File meta with Action ID failed: ${e.message}`, e); - } - - // update cases - await updateCases({ casesClient, createActionPayload, endpointData }); - - return res.ok({ - body: { - action: actionId, - data, - }, - }); - } catch (err) { - if (uploadParameters.file_id) { - // Try to delete the created file since creating the action threw an error - try { - await fleetFiles.delete(uploadParameters.file_id); - } catch (e) { - logger.error( - `Attempt to clean up file (after action creation was unsuccessful) failed; ${e.message}`, - e - ); - } - } - - return errorHandler(logger, res, err); - } - }; -}; 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 dd10093cc343c..8f7682c83daad 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 @@ -34,6 +34,7 @@ import { UNISOLATE_HOST_ROUTE, GET_FILE_ROUTE, EXECUTE_ROUTE, + UPLOAD_ROUTE, } from '../../../../common/endpoint/constants'; import type { ActionDetails, @@ -46,7 +47,9 @@ import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data' import type { EndpointAuthz } from '../../../../common/endpoint/types/authz'; import type { SecuritySolutionRequestHandlerContextMock } from '../../../lib/detection_engine/routes/__mocks__/request_context'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; +import type { HttpApiTestSetupMock } from '../../mocks'; import { + createHttpApiTestSetupMock, createMockEndpointAppContext, createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, @@ -59,6 +62,13 @@ import * as ActionDetailsService from '../../services/actions/action_details_by_ import { CaseStatuses } from '@kbn/cases-components'; import { getEndpointAuthzInitialStateMock } from '../../../../common/endpoint/service/authz/mocks'; import { actionCreateService } from '../../services/actions'; +import type { UploadActionApiRequestBody } from '../../../../common/api/endpoint'; +import type { FleetToHostFileClientInterface } from '@kbn/fleet-plugin/server'; +import type { HapiReadableStream, SecuritySolutionRequestHandlerContext } from '../../../types'; +import { createHapiReadableStreamMock } from '../../services/actions/mocks'; +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; +import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; +import { omit } from 'lodash'; interface CallRouteInterface { body?: ResponseActionRequestBody; @@ -827,26 +837,26 @@ describe('Response actions', () => { }); it('handles errors', async () => { - const errMessage = 'Uh oh!'; - await callRoute( - UNISOLATE_HOST_ROUTE_V2, - { - body: { endpoint_ids: ['XYZ'] }, - version: '2023-10-31', - indexErrorResponse: { - statusCode: 500, - body: { - result: errMessage, + const expectedError = new Error('Uh oh!'); + + await expect( + callRoute( + UNISOLATE_HOST_ROUTE_V2, + { + body: { endpoint_ids: ['XYZ'] }, + version: '2023-10-31', + indexErrorResponse: { + statusCode: 500, + body: { + result: expectedError.message, + }, }, }, - }, - { endpointDsExists: true } - ); + { endpointDsExists: true } + ) + ).rejects.toEqual(expectedError); expect(mockResponse.ok).not.toBeCalled(); - const response = mockResponse.customError.mock.calls[0][0]; - expect(response.statusCode).toEqual(500); - expect((response.body as Error).message).toEqual(errMessage); }); }); @@ -1001,4 +1011,140 @@ describe('Response actions', () => { }); }); }); + + describe('Upload response action handler', () => { + type UploadHttpApiTestSetupMock = HttpApiTestSetupMock< + never, + never, + UploadActionApiRequestBody + >; + type UploadRequestHandler = RequestHandler< + never, + never, + UploadActionApiRequestBody, + SecuritySolutionRequestHandlerContext + >; + + let testSetup: UploadHttpApiTestSetupMock; + let httpRequestMock: ReturnType; + let httpHandlerContextMock: UploadHttpApiTestSetupMock['httpHandlerContextMock']; + let httpResponseMock: UploadHttpApiTestSetupMock['httpResponseMock']; + let fleetFilesClientMock: jest.Mocked; + let callHandler: () => ReturnType; + let fileContent: HapiReadableStream; + let createdUploadAction: ActionDetails; + + beforeEach(async () => { + testSetup = createHttpApiTestSetupMock(); + + ({ httpHandlerContextMock, httpResponseMock } = testSetup); + httpRequestMock = testSetup.createRequestMock(); + + fleetFilesClientMock = + (await testSetup.endpointAppContextMock.service.getFleetToHostFilesClient()) as jest.Mocked; + + fileContent = createHapiReadableStreamMock(); + + const reqBody: UploadActionApiRequestBody = { + file: fileContent, + endpoint_ids: ['123-456'], + parameters: { + overwrite: true, + }, + }; + + httpRequestMock = testSetup.createRequestMock({ body: reqBody }); + registerResponseActionRoutes(testSetup.routerMock, testSetup.endpointAppContextMock); + + createdUploadAction = new EndpointActionGenerator('seed').generateActionDetails({ + command: 'upload', + }); + + ( + testSetup.endpointAppContextMock.service.getActionCreateService().createAction as jest.Mock + ).mockResolvedValue(createdUploadAction); + + (testSetup.endpointAppContextMock.service.getEndpointMetadataService as jest.Mock) = jest + .fn() + .mockReturnValue({ + getMetadataForEndpoints: jest.fn().mockResolvedValue([ + { + elastic: { + agent: { + id: '123-456', + }, + }, + }, + ]), + }); + + const handler = testSetup.getRegisteredVersionedRoute('post', UPLOAD_ROUTE, '2023-10-31') + .routeHandler as UploadRequestHandler; + + callHandler = () => handler(httpHandlerContextMock, httpRequestMock, httpResponseMock); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should create a file', async () => { + await callHandler(); + + expect(fleetFilesClientMock.create).toHaveBeenCalledWith(fileContent, ['123-456']); + }); + + it('should create the action using parameters with stored file info', async () => { + await callHandler(); + + const createActionMock = testSetup.endpointAppContextMock.service.getActionCreateService() + .createAction as jest.Mock; + + expect(createActionMock).toHaveBeenCalledWith( + { + command: 'upload', + endpoint_ids: ['123-456'], + parameters: { + file_id: '123-456-789', + file_name: 'foo.txt', + file_sha256: '96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3', + file_size: 45632, + overwrite: true, + }, + user: { username: 'unknown' }, + }, + ['123-456'] + ); + }); + + it('should delete file if creation of Action fails', async () => { + const createActionMock = testSetup.endpointAppContextMock.service.getActionCreateService() + .createAction as jest.Mock; + createActionMock.mockImplementation(async () => { + throw new CustomHttpRequestError('oh oh'); + }); + await callHandler(); + + expect(fleetFilesClientMock.delete).toHaveBeenCalledWith('123-456-789'); + }); + + it('should update file with action id', async () => { + await callHandler(); + + expect(fleetFilesClientMock.update).toHaveBeenCalledWith('123-456-789', { + actionId: '123', + }); + }); + + it('should return expected response on success', async () => { + await callHandler(); + + expect(httpResponseMock.ok).toHaveBeenCalledWith({ + body: { + action: createdUploadAction.action, + data: omit(createdUploadAction, 'action'), + }, + }); + }); + }); }); 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 3b98efa12cd98..c8669869833fe 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 @@ -7,9 +7,14 @@ import type { RequestHandler } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; +import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; +import { EndpointActionsClient } from '../../services/actions/clients'; import type { - ResponseActionBodySchema, NoParametersRequestSchema, + ResponseActionsRequestBody, + ExecuteActionRequestBody, + ResponseActionGetFileRequestBody, + UploadActionApiRequestBody, } from '../../../../common/api/endpoint'; import { ExecuteActionRequestSchema, @@ -19,6 +24,7 @@ import { SuspendProcessRouteRequestSchema, UnisolateRouteRequestSchema, GetProcessesRouteRequestSchema, + UploadActionRequestSchema, } from '../../../../common/api/endpoint'; import { @@ -31,13 +37,14 @@ import { UNISOLATE_HOST_ROUTE, GET_FILE_ROUTE, EXECUTE_ROUTE, + UPLOAD_ROUTE, } from '../../../../common/endpoint/constants'; import type { EndpointActionDataParameterTypes, ResponseActionParametersWithPidOrEntityId, ResponseActionsExecuteParameters, ActionDetails, - HostMetadata, + KillOrSuspendProcessRequestBody, } from '../../../../common/endpoint/types'; import type { ResponseActionsApiCommandNames } from '../../../../common/endpoint/service/response_actions/constants'; import type { @@ -46,8 +53,7 @@ import type { } from '../../../types'; import type { EndpointAppContext } from '../../types'; import { withEndpointAuthz } from '../with_endpoint_authz'; -import { registerActionFileUploadRoute } from './file_upload_handler'; -import { updateCases } from '../../services/actions/create/update_cases'; +import { errorHandler } from '../error_handler'; export function registerResponseActionRoutes( router: SecuritySolutionPluginRouter, @@ -243,7 +249,33 @@ export function registerResponseActionRoutes( ) ); - registerActionFileUploadRoute(router, endpointContext); + router.versioned + .post({ + access: 'public', + path: UPLOAD_ROUTE, + options: { + authRequired: true, + tags: ['access:securitySolution'], + body: { + accepts: ['multipart/form-data'], + output: 'stream', + maxBytes: endpointContext.serverConfig.maxUploadResponseActionFileBytes, + }, + }, + }) + .addVersion( + { + version: '2023-10-31', + validate: { + request: UploadActionRequestSchema, + }, + }, + withEndpointAuthz( + { all: ['canWriteFileOperations'] }, + logger, + responseActionRequestHandler(endpointContext, 'upload') + ) + ); } function responseActionRequestHandler( @@ -252,43 +284,76 @@ function responseActionRequestHandler, + ResponseActionsRequestBody, SecuritySolutionRequestHandlerContext > { + const logger = endpointContext.logFactory.get('responseActionsHandler'); + return async (context, req, res) => { const user = endpointContext.service.security?.authc.getCurrentUser(req); const esClient = (await context.core).elasticsearch.client.asInternalUser; - - let action: ActionDetails; + const casesClient = await endpointContext.service.getCasesClient(req); + const actionsClient = new EndpointActionsClient({ + esClient, + casesClient, + endpointContext, + username: user?.username ?? 'unknown', + }); try { - const createActionPayload = { ...req.body, command, user }; - const endpointData = await endpointContext.service - .getEndpointMetadataService() - .getMetadataForEndpoints(esClient, [...new Set(createActionPayload.endpoint_ids)]); - const agentIds = endpointData.map((endpoint: HostMetadata) => endpoint.elastic.agent.id); + let action: ActionDetails; - action = await endpointContext.service - .getActionCreateService() - .createAction(createActionPayload, agentIds); + switch (command) { + case 'isolate': + action = await actionsClient.isolate(req.body); + break; - // update cases - const casesClient = await endpointContext.service.getCasesClient(req); - await updateCases({ casesClient, createActionPayload, endpointData }); - } catch (err) { - return res.customError({ - statusCode: 500, - body: err, + case 'unisolate': + action = await actionsClient.release(req.body); + break; + + case 'running-processes': + action = await actionsClient.runningProcesses(req.body); + break; + + case 'execute': + action = await actionsClient.execute(req.body as ExecuteActionRequestBody); + break; + + case 'suspend-process': + action = await actionsClient.suspendProcess(req.body as KillOrSuspendProcessRequestBody); + break; + + case 'kill-process': + action = await actionsClient.killProcess(req.body as KillOrSuspendProcessRequestBody); + break; + + case 'get-file': + action = await actionsClient.getFile(req.body as ResponseActionGetFileRequestBody); + break; + + case 'upload': + action = await actionsClient.upload(req.body as UploadActionApiRequestBody); + break; + + default: + throw new CustomHttpRequestError( + `No handler found for response action command: [${command}]`, + 501 + ); + } + + const { action: actionId, ...data } = action; + + return res.ok({ + body: { + action: actionId, + data, + }, }); + } catch (err) { + return errorHandler(logger, res, err); } - - const { action: actionId, ...data } = action; - return res.ok({ - body: { - action: actionId, - data, - }, - }); }; } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts new file mode 100644 index 0000000000000..2b5813fa6c909 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts @@ -0,0 +1,215 @@ +/* + * Copyright 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 { HapiReadableStream } from '../../../../types'; +import type { ResponseActionsApiCommandNames } from '../../../../../common/endpoint/service/response_actions/constants'; +import { updateCases } from '../create/update_cases'; +import type { CreateActionPayload } from '../create/types'; +import type { + ExecuteActionRequestBody, + GetProcessesRequestBody, + IsolationRouteRequestBody, + ResponseActionGetFileRequestBody, + UploadActionApiRequestBody, + ResponseActionsRequestBody, +} from '../../../../../common/api/endpoint'; +import { BaseResponseActionsClient } from '../../../lib/response_actions/base_actions_provider'; +import type { + ActionDetails, + HostMetadata, + GetProcessesActionOutputContent, + KillOrSuspendProcessRequestBody, + KillProcessActionOutputContent, + ResponseActionExecuteOutputContent, + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters, + ResponseActionParametersWithPidOrEntityId, + ResponseActionsExecuteParameters, + ResponseActionUploadOutputContent, + ResponseActionUploadParameters, + SuspendProcessActionOutputContent, + HostMetadataInterface, + ImmutableObject, +} from '../../../../../common/endpoint/types'; + +export class EndpointActionsClient extends BaseResponseActionsClient { + private async checkAgentIds(ids: string[]): Promise<{ + valid: string[]; + invalid: string[]; + allValid: boolean; + hosts: HostMetadata[]; + }> { + const foundEndpointHosts = await this.options.endpointContext.service + .getEndpointMetadataService() + .getMetadataForEndpoints(this.options.esClient, [...new Set(ids)]); + const validIds = foundEndpointHosts.map((endpoint: HostMetadata) => endpoint.elastic.agent.id); + const invalidIds = ids.filter((id) => !validIds.includes(id)); + + if (invalidIds.length) { + this.log.debug(`The following agent ids are not valid: ${JSON.stringify(invalidIds)}`); + } + + return { + valid: validIds, + invalid: invalidIds, + allValid: invalidIds.length === 0, + hosts: foundEndpointHosts, + }; + } + + private async handleResponseAction< + TOptions extends ResponseActionsRequestBody = ResponseActionsRequestBody, + TResponse extends ActionDetails = ActionDetails + >(command: ResponseActionsApiCommandNames, options: TOptions): Promise { + const agentIds = await this.checkAgentIds(options.endpoint_ids); + const createPayload: CreateActionPayload = { + ...options, + command, + user: { username: this.options.username }, + }; + + const response = await this.options.endpointContext.service + .getActionCreateService() + .createAction(createPayload, agentIds.valid); + + await this.updateCases(createPayload, agentIds.hosts); + + return response as TResponse; + } + + protected async updateCases( + createActionPayload: CreateActionPayload, + endpointData: Array> + ): Promise { + return updateCases({ + casesClient: this.options.casesClient, + createActionPayload, + endpointData, + }); + } + + async isolate(options: IsolationRouteRequestBody): Promise { + return this.handleResponseAction('isolate', options); + } + + async release(options: IsolationRouteRequestBody): Promise { + return this.handleResponseAction( + 'unisolate', + options + ); + } + + async killProcess( + options: KillOrSuspendProcessRequestBody + ): Promise< + ActionDetails + > { + return this.handleResponseAction< + KillOrSuspendProcessRequestBody, + ActionDetails + >('kill-process', options); + } + + async suspendProcess( + options: KillOrSuspendProcessRequestBody + ): Promise< + ActionDetails + > { + return this.handleResponseAction< + KillOrSuspendProcessRequestBody, + ActionDetails + >('suspend-process', options); + } + + async runningProcesses( + options: GetProcessesRequestBody + ): Promise> { + return this.handleResponseAction< + GetProcessesRequestBody, + ActionDetails + >('running-processes', options); + } + + async getFile( + options: ResponseActionGetFileRequestBody + ): Promise> { + return this.handleResponseAction< + ResponseActionGetFileRequestBody, + ActionDetails + >('get-file', options); + } + + async execute( + options: ExecuteActionRequestBody + ): Promise> { + return this.handleResponseAction< + ExecuteActionRequestBody, + ActionDetails + >('execute', options); + } + + async upload( + options: UploadActionApiRequestBody + ): Promise> { + const fleetFiles = await this.options.endpointContext.service.getFleetToHostFilesClient(); + const fileStream = options.file as HapiReadableStream; + const { file: _, parameters: userParams, ...actionPayload } = options; + const uploadParameters: ResponseActionUploadParameters = { + ...userParams, + file_id: '', + file_name: '', + file_sha256: '', + file_size: 0, + }; + + const createdFile = await fleetFiles.create(fileStream, actionPayload.endpoint_ids); + + uploadParameters.file_id = createdFile.id; + uploadParameters.file_name = createdFile.name; + uploadParameters.file_sha256 = createdFile.sha256; + uploadParameters.file_size = createdFile.size; + + const createFileActionOptions = { + ...actionPayload, + parameters: uploadParameters, + command: 'upload' as ResponseActionsApiCommandNames, + }; + + try { + const createdAction = await this.handleResponseAction< + typeof createFileActionOptions, + ActionDetails + >('upload', createFileActionOptions); + + // Update the file meta to include the action id, and if any errors (unlikely), + // then just log them and still allow api to return success since the action has + // already been created and potentially dispatched to Endpoint. Action ID is not + // needed by the Endpoint or fleet-server's API, so no need to fail here + try { + await fleetFiles.update(uploadParameters.file_id, { actionId: createdAction.id }); + } catch (e) { + this.log.warn(`Attempt to update File meta with Action ID failed: ${e.message}`, e); + } + + return createdAction; + } catch (err) { + if (uploadParameters.file_id) { + // Try to delete the created file since creating the action threw an error + try { + await fleetFiles.delete(uploadParameters.file_id); + } catch (e) { + this.log.error( + `Attempt to clean up file id [${uploadParameters.file_id}] (after action creation was unsuccessful) failed; ${e.message}`, + e + ); + } + } + + throw err; + } + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts new file mode 100644 index 0000000000000..83a11352dfe95 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { EndpointActionsClient } from './endpoint_actions_client'; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/types.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/types.ts index aef5082bbb4a5..f8a10b18d594d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/types.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { AuthenticationServiceStart } from '@kbn/security-plugin/server'; import type { LicenseType } from '@kbn/licensing-plugin/server'; import type { TypeOf } from '@kbn/config-schema'; import type { ResponseActionBodySchema } from '../../../../../common/api/endpoint'; @@ -17,7 +16,7 @@ import type { ResponseActionsApiCommandNames } from '../../../../../common/endpo export type CreateActionPayload = TypeOf & { command: ResponseActionsApiCommandNames; - user?: ReturnType; + user?: { username: string } | null | undefined; rule_id?: string; rule_name?: string; error?: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts index e410146505545..95a8687324d1c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts @@ -6,6 +6,7 @@ */ import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; +import { requiredOptional } from '@kbn/zod-helpers'; import { DEFAULT_MAX_SIGNALS } from '../../../../../../../common/constants'; import { assertUnreachable } from '../../../../../../../common/utility_types'; import type { @@ -123,7 +124,7 @@ const extractDiffableCommonFields = ( severity: rule.severity, severity_mapping: rule.severity_mapping ?? [], risk_score: rule.risk_score, - risk_score_mapping: rule.risk_score_mapping ?? [], + risk_score_mapping: rule.risk_score_mapping?.map((mapping) => requiredOptional(mapping)) ?? [], // About -> Advanced settings references: rule.references ?? [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts index 0ec1d5580f40b..06ccf0a2b97f4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts @@ -17,7 +17,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 26 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 2 more"` ); }); @@ -40,7 +40,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, version: Required, and 25 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more"` ); }); @@ -177,7 +177,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"index.0: Expected string, received number"` ); }); @@ -201,7 +201,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"filters: Expected array, received string"` ); }); @@ -236,7 +236,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + `"language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'"` ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index f65d788945deb..cb0b48b8e3f0d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -35,6 +35,8 @@ import type { import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks'; import type { EndpointAuthz } from '../../../../../common/endpoint/types/authz'; import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/risk_engine_data_client.mock'; +import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock'; +import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; export const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -63,6 +65,8 @@ export const createMockClients = () => { detectionEngineHealthClient: detectionEngineHealthClientMock.create(), ruleExecutionLog: ruleExecutionLogMock.forRoutes.create(), riskEngineDataClient: riskEngineDataClientMock.create(), + riskScoreDataClient: riskScoreDataClientMock.create(), + assetCriticalityDataClient: assetCriticalityDataClientMock.create(), }; }; @@ -142,6 +146,8 @@ const createSecuritySolutionRequestContextMock = ( throw new Error('Not implemented'); }), getRiskEngineDataClient: jest.fn(() => clients.riskEngineDataClient), + getRiskScoreDataClient: jest.fn(() => clients.riskScoreDataClient), + getAssetCriticalityDataClient: jest.fn(() => clients.assetCriticalityDataClient), }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts index a1d74b1445508..f45f005c7c34b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts @@ -237,7 +237,7 @@ describe('Create rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "saved_query", saved_id: Required, type: Invalid literal value, expected "threshold", and 18 more' + 'response_actions.0.action_type_id: Invalid literal value, expected ".osquery", response_actions.0.params.command: Invalid literal value, expected "isolate"' ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts index f95b10fa6154f..6bdafa76fd9b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts @@ -284,7 +284,7 @@ describe('Update rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "saved_query", saved_id: Required, type: Invalid literal value, expected "threshold", and 18 more' + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\"` ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts index be9561598fd08..b6cffd47a494e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts @@ -283,7 +283,7 @@ describe('create_rules_stream_from_ndjson', () => { immutable: false, }); expect(resultOrError[1].message).toContain( - 'name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 25 more' + `name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more` ); expect(resultOrError[2]).toEqual({ rule_id: 'rule-2', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index a513e8468d577..8fa275c7ba59f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -95,7 +95,7 @@ describe('validate', () => { delete ruleAlert.name; expect(() => { transformValidate(ruleAlert); - }).toThrowError('Invalid input'); + }).toThrowError('Required'); }); }); @@ -113,8 +113,7 @@ describe('validate', () => { const validatedOrError = transformValidateBulkError('rule-1', ruleAlert); const expected: BulkError = { error: { - message: - 'name: Required, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", name: Required, name: Required, and 22 more', + message: 'name: Required', status_code: 500, }, rule_id: 'rule-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts index c8dfa113af6a4..5852e6f964c7b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts @@ -7,6 +7,7 @@ import { each, map, some, uniq } from 'lodash'; import { containsDynamicQuery } from '@kbn/osquery-plugin/common/utils/replace_params_query'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { ResponseActionAlerts } from './types'; import type { SetupPlugins } from '../../../plugin_contract'; import type { RuleResponseOsqueryAction } from '../../../../common/api/detection_engine/model/rule_response_actions'; @@ -32,7 +33,7 @@ export const osqueryResponseAction = ( return osqueryCreateActionService.create({ ...rest, - queries, + queries: requiredOptional(queries), ecs_mapping: ecsMapping, saved_query_id: savedQueryId, agent_ids: agentIds, @@ -43,7 +44,7 @@ export const osqueryResponseAction = ( return osqueryCreateActionService.create( { ...rest, - queries, + queries: requiredOptional(queries), ecs_mapping: ecsMapping, saved_query_id: savedQueryId, agent_ids: alert.agent?.id ? [alert.agent.id] : [], 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 2309833a947f0..7641c71b28dbf 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,6 +43,7 @@ import { TIMESTAMP, } from '@kbn/rule-data-utils'; import { flattenWithPrefix } from '@kbn/securitysolution-rules'; +import { requiredOptional } from '@kbn/zod-helpers'; import { createHash } from 'crypto'; @@ -229,7 +230,7 @@ export const buildAlert = ( [ALERT_RULE_NAMESPACE_FIELD]: params.namespace, [ALERT_RULE_NOTE]: params.note, [ALERT_RULE_REFERENCES]: params.references, - [ALERT_RULE_RISK_SCORE_MAPPING]: params.riskScoreMapping, + [ALERT_RULE_RISK_SCORE_MAPPING]: requiredOptional(params.riskScoreMapping), [ALERT_RULE_RULE_ID]: params.ruleId, [ALERT_RULE_RULE_NAME_OVERRIDE]: params.ruleNameOverride, [ALERT_RULE_SEVERITY_MAPPING]: params.severityMapping, 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 50df9b714c3ca..fb1aa57fdb82d 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 @@ -7,6 +7,7 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { BaseHit, SearchTypes } from '../../../../../../common/detection_engine/types'; import type { ConfigType } from '../../../../../config'; @@ -92,7 +93,7 @@ export const buildBulkBody = ( riskScoreOverride: buildRiskScoreFromMapping({ eventSource: mergedDoc._source ?? {}, riskScore: completeRule.ruleParams.riskScore, - riskScoreMapping: completeRule.ruleParams.riskScoreMapping, + riskScoreMapping: requiredOptional(completeRule.ruleParams.riskScoreMapping), }).riskScore, } : undefined; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.mock.ts new file mode 100644 index 0000000000000..eaf3e9a3ec22e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AssetCriticalityDataClient } from './asset_criticality_data_client'; + +const createAssetCriticalityDataClientMock = () => + ({ + doesIndexExist: jest.fn(), + getStatus: jest.fn(), + init: jest.fn(), + } as unknown as jest.Mocked); + +export const assetCriticalityDataClientMock = { create: createAssetCriticalityDataClientMock }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.test.ts new file mode 100644 index 0000000000000..c06586516d682 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { AssetCriticalityDataClient } from './asset_criticality_data_client'; + +import { createOrUpdateIndex } from '../utils/create_or_update_index'; + +jest.mock('../utils/create_or_update_index', () => ({ + createOrUpdateIndex: jest.fn(), +})); + +describe('AssetCriticalityDataClient', () => { + const esClientInternal = elasticsearchServiceMock.createScopedClusterClient().asInternalUser; + const logger = loggingSystemMock.createLogger(); + describe('init', () => { + it('ensures the index is available and up to date', async () => { + const assetCriticalityDataClient = new AssetCriticalityDataClient({ + esClient: esClientInternal, + logger, + namespace: 'default', + }); + + await assetCriticalityDataClient.init(); + + expect(createOrUpdateIndex).toHaveBeenCalledWith({ + esClient: esClientInternal, + logger, + options: { + index: '.asset-criticality.asset-criticality-default', + mappings: { + dynamic: 'strict', + properties: { + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + criticality_level: { + type: 'keyword', + }, + '@timestamp': { + type: 'date', + ignore_malformed: false, + }, + updated_at: { + type: 'date', + }, + }, + }, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts new file mode 100644 index 0000000000000..4ac8f43627432 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.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 type { Logger, ElasticsearchClient } from '@kbn/core/server'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; +import { createOrUpdateIndex } from '../utils/create_or_update_index'; +import { getAssetCriticalityIndex } from '../../../../common/asset_criticality'; +import { assetCriticalityFieldMap } from './configurations'; + +interface AssetCriticalityClientOpts { + logger: Logger; + esClient: ElasticsearchClient; + namespace: string; +} + +export class AssetCriticalityDataClient { + constructor(private readonly options: AssetCriticalityClientOpts) {} + /** + * It will create idex for asset criticality, + * or update mappings if index exists + */ + public async init() { + await createOrUpdateIndex({ + esClient: this.options.esClient, + logger: this.options.logger, + options: { + index: getAssetCriticalityIndex(this.options.namespace), + mappings: mappingFromFieldMap(assetCriticalityFieldMap, 'strict'), + }, + }); + } + + public async doesIndexExist() { + try { + const result = await this.options.esClient.indices.exists({ + index: getAssetCriticalityIndex(this.options.namespace), + }); + return result; + } catch (e) { + return false; + } + } + + public async getStatus() { + const isAssetCriticalityResourcesInstalled = await this.doesIndexExist(); + + return { + isAssetCriticalityResourcesInstalled, + }; + } +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.test.ts new file mode 100644 index 0000000000000..b0afd8e359526 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.test.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 { loggingSystemMock } from '@kbn/core/server/mocks'; +import { requestContextMock } from '../../detection_engine/routes/__mocks__'; +import { AssetCriticalityDataClient } from './asset_criticality_data_client'; +import { checkAndInitAssetCriticalityResources } from './check_and_init_asset_criticality_resources'; + +describe('checkAndInitAssetCriticalityResources', () => { + const logger = loggingSystemMock.createLogger(); + const { context } = requestContextMock.createTools(); + const doesIndexExist = jest.spyOn(AssetCriticalityDataClient.prototype, 'doesIndexExist'); + const initAssetCriticality = jest.spyOn(AssetCriticalityDataClient.prototype, 'init'); + + beforeEach(() => { + doesIndexExist.mockImplementation(() => Promise.resolve(false)); + initAssetCriticality.mockImplementation(() => Promise.resolve()); + }); + + afterEach(() => { + doesIndexExist.mockReset(); + initAssetCriticality.mockReset(); + }); + + it('should initialise asset criticality resources if they do not exist', async () => { + await checkAndInitAssetCriticalityResources(requestContextMock.convertContext(context), logger); + + expect(initAssetCriticality).toHaveBeenCalled(); + expect(logger.info).toHaveBeenCalledWith('Asset criticality resources installed'); + }); + + it('should not initialise asset criticality resources if they already exist', async () => { + doesIndexExist.mockImplementationOnce(() => Promise.resolve(true)); + await checkAndInitAssetCriticalityResources(requestContextMock.convertContext(context), logger); + + expect(initAssetCriticality).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.ts new file mode 100644 index 0000000000000..08843c90a1739 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/check_and_init_asset_criticality_resources.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 { Logger } from '@kbn/core/server'; +import { AssetCriticalityDataClient } from './asset_criticality_data_client'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; + +/** + * As internal user we check for existence of asset crititcality resources + * and initialise it if it does not exist + * @param context + * @param logger + */ +export const checkAndInitAssetCriticalityResources = async ( + context: SecuritySolutionRequestHandlerContext, + logger: Logger +) => { + const securityContext = await context.securitySolution; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; + + const assetCriticalityDataClient = new AssetCriticalityDataClient({ + esClient, + logger, + namespace: securityContext.getSpaceId(), + }); + + const doesIndexExist = await assetCriticalityDataClient.doesIndexExist(); + + if (!doesIndexExist) { + logger.info('Asset criticality resources are not installed, initialising...'); + await assetCriticalityDataClient.init(); + logger.info('Asset criticality resources installed'); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/configurations.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/configurations.ts new file mode 100644 index 0000000000000..c1b309c3a2f44 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/configurations.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { FieldMap } from '@kbn/alerts-as-data-utils'; + +export const assetCriticalityFieldMap: FieldMap = { + '@timestamp': { + type: 'date', + array: false, + required: false, + }, + id_field: { + type: 'keyword', + array: false, + required: false, + }, + id_value: { + type: 'keyword', + array: false, + required: false, + }, + criticality_level: { + type: 'keyword', + array: false, + required: false, + }, + updated_at: { + type: 'date', + array: false, + required: false, + }, +} as const; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts new file mode 100644 index 0000000000000..c72249c3110e3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { assetCriticalityStatusRoute } from './status'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts new file mode 100644 index 0000000000000..7605d2cb099cc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { ASSET_CRITICALITY_STATUS_URL, APP_ID } from '../../../../../common/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; + +export const assetCriticalityStatusRoute = ( + router: SecuritySolutionPluginRouter, + logger: Logger +) => { + router.versioned + .get({ + access: 'internal', + path: ASSET_CRITICALITY_STATUS_URL, + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion({ version: '1', validate: {} }, async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + await checkAndInitAssetCriticalityResources(context, logger); + + const securitySolution = await context.securitySolution; + const assetCriticalityClient = securitySolution.getAssetCriticalityDataClient(); + + const result = await assetCriticalityClient.getStatus(); + return response.ok({ + body: { + asset_criticality_resources_installed: result.isAssetCriticalityResourcesInstalled, + }, + }); + } catch (e) { + const error = transformError(e); + + return siemResponse.error({ + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, + bypassErrorFormat: true, + }); + } + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.ts new file mode 100644 index 0000000000000..d399b628c4adf --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core/server'; +import type { SecurityPluginStart } from '@kbn/security-plugin/server'; +import type { RiskEnginePrivilegesResponse } from '../types'; +import { + RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, +} from '../../../../common/risk_engine'; + +const groupPrivilegesByName = ( + privileges: Array<{ + privilege: PrivilegeName; + authorized: boolean; + }> +): Record => { + return privileges.reduce>((acc, { privilege, authorized }) => { + acc[privilege] = authorized; + return acc; + }, {}); +}; + +export async function getUserRiskEnginePrivileges( + request: KibanaRequest, + security: SecurityPluginStart +): Promise { + const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(request); + const { privileges, hasAllRequested } = await checkPrivileges({ + elasticsearch: { + cluster: RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + index: RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, + }, + }); + + const clusterPrivilegesByPrivilege = groupPrivilegesByName(privileges.elasticsearch.cluster); + + const indexPrivilegesByIndex = Object.entries(privileges.elasticsearch.index).reduce< + Record> + >((acc, [index, indexPrivileges]) => { + acc[index] = groupPrivilegesByName(indexPrivileges); + return acc; + }, {}); + + return { + privileges: { + elasticsearch: { + cluster: clusterPrivilegesByPrivilege, + index: indexPrivilegesByIndex, + }, + }, + has_all_required: hasAllRequested, + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts index 53c7291aa497c..a8d7b7a9c763b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts @@ -13,11 +13,8 @@ const createRiskEngineDataClientMock = () => disableRiskEngine: jest.fn(), enableRiskEngine: jest.fn(), getConfiguration: jest.fn(), - getRiskInputsIndex: jest.fn(), getStatus: jest.fn(), - getWriter: jest.fn(), init: jest.fn(), - initializeResources: jest.fn(), } as unknown as jest.Mocked); export const riskEngineDataClientMock = { create: createRiskEngineDataClientMock }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts index 619952859fc0f..a155a320cb606 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts @@ -5,10 +5,6 @@ * 2.0. */ -import { - createOrUpdateComponentTemplate, - createOrUpdateIndexTemplate, -} from '@kbn/alerting-plugin/server'; import { loggingSystemMock, elasticsearchServiceMock, @@ -17,11 +13,11 @@ import { import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import type { SavedObject } from '@kbn/core/server'; import { RiskEngineDataClient } from './risk_engine_data_client'; -import type { RiskEngineConfiguration } from './types'; -import { createDataStream } from './utils/create_datastream'; +import { RiskScoreDataClient } from '../risk_score/risk_score_data_client'; +import type { RiskEngineConfiguration } from '../types'; import * as savedObjectConfig from './utils/saved_object_configuration'; -import * as transforms from './utils/transforms'; -import { createIndex } from './utils/create_index'; +import * as transforms from '../utils/transforms'; +import { riskScoreDataClientMock } from '../risk_score/risk_score_data_client.mock'; const getSavedObjectConfiguration = (attributes = {}) => ({ page: 1, @@ -47,32 +43,17 @@ const getSavedObjectConfiguration = (attributes = {}) => ({ ], }); -const transformsMock = { - count: 1, - transforms: [ - { - id: 'ml_hostriskscore_pivot_transform_default', - dest: { index: '' }, - source: { index: '' }, - }, - ], -}; - jest.mock('@kbn/alerting-plugin/server', () => ({ createOrUpdateComponentTemplate: jest.fn(), createOrUpdateIndexTemplate: jest.fn(), })); -jest.mock('./utils/create_datastream', () => ({ +jest.mock('../utils/create_datastream', () => ({ createDataStream: jest.fn(), })); -jest.mock('../../risk_score/transform/helpers/transforms', () => ({ - createAndStartTransform: jest.fn(), -})); - -jest.mock('./utils/create_index', () => ({ - createIndex: jest.fn(), +jest.mock('../utils/create_or_update_index', () => ({ + createOrUpdateIndex: jest.fn(), })); jest.spyOn(transforms, 'createTransform').mockResolvedValue(Promise.resolve()); @@ -87,7 +68,6 @@ describe('RiskEngineDataClient', () => { let mockSavedObjectClient: ReturnType; let logger: ReturnType; const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; - const totalFieldsLimit = 1000; beforeEach(() => { logger = loggingSystemMock.createLogger(); @@ -106,484 +86,6 @@ describe('RiskEngineDataClient', () => { jest.clearAllMocks(); }); - describe('getWriter', () => { - it('should return a writer object', async () => { - const writer = await riskEngineDataClient.getWriter({ namespace: 'default' }); - expect(writer).toBeDefined(); - expect(typeof writer?.bulk).toBe('function'); - }); - - it('should cache and return the same writer for the same namespace', async () => { - const writer1 = await riskEngineDataClient.getWriter({ namespace: 'default' }); - const writer2 = await riskEngineDataClient.getWriter({ namespace: 'default' }); - const writer3 = await riskEngineDataClient.getWriter({ namespace: 'space-1' }); - - expect(writer1).toEqual(writer2); - expect(writer2).not.toEqual(writer3); - }); - }); - - describe('initializeResources success', () => { - it('should initialize risk engine resources', async () => { - await riskEngineDataClient.initializeResources({ namespace: 'default' }); - - expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( - expect.objectContaining({ - logger, - esClient, - template: expect.objectContaining({ - name: '.risk-score-mappings', - _meta: { - managed: true, - }, - }), - totalFieldsLimit: 1000, - }) - ); - expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) - .toMatchInlineSnapshot(` - Object { - "mappings": Object { - "dynamic": "strict", - "properties": Object { - "@timestamp": Object { - "ignore_malformed": false, - "type": "date", - }, - "host": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, - }, - "type": "object", - }, - "notes": Object { - "type": "keyword", - }, - }, - "type": "object", - }, - }, - }, - "user": Object { - "properties": Object { - "name": Object { - "type": "keyword", - }, - "risk": Object { - "properties": Object { - "calculated_level": Object { - "type": "keyword", - }, - "calculated_score": Object { - "type": "float", - }, - "calculated_score_norm": Object { - "type": "float", - }, - "category_1_count": Object { - "type": "long", - }, - "category_1_score": Object { - "type": "float", - }, - "id_field": Object { - "type": "keyword", - }, - "id_value": Object { - "type": "keyword", - }, - "inputs": Object { - "properties": Object { - "category": Object { - "type": "keyword", - }, - "description": Object { - "type": "keyword", - }, - "id": Object { - "type": "keyword", - }, - "index": Object { - "type": "keyword", - }, - "risk_score": Object { - "type": "float", - }, - "timestamp": Object { - "type": "date", - }, - }, - "type": "object", - }, - "notes": Object { - "type": "keyword", - }, - }, - "type": "object", - }, - }, - }, - }, - }, - "settings": Object {}, - } - `); - - expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ - logger, - esClient, - template: { - name: '.risk-score.risk-score-default-index-template', - body: { - data_stream: { hidden: true }, - index_patterns: ['risk-score.risk-score-default'], - composed_of: ['.risk-score-mappings'], - template: { - lifecycle: {}, - settings: { - 'index.mapping.total_fields.limit': totalFieldsLimit, - }, - mappings: { - dynamic: false, - _meta: { - kibana: { - version: '8.9.0', - }, - managed: true, - namespace: 'default', - }, - }, - }, - _meta: { - kibana: { - version: '8.9.0', - }, - managed: true, - namespace: 'default', - }, - }, - }, - }); - - expect(createDataStream).toHaveBeenCalledWith({ - logger, - esClient, - totalFieldsLimit, - indexPatterns: { - template: `.risk-score.risk-score-default-index-template`, - alias: `risk-score.risk-score-default`, - }, - }); - - expect(createIndex).toHaveBeenCalledWith({ - logger, - esClient, - options: { - index: `risk-score.risk-score-latest-default`, - mappings: { - dynamic: 'strict', - properties: { - '@timestamp': { - ignore_malformed: false, - type: 'date', - }, - host: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', - }, - }, - type: 'object', - }, - notes: { - type: 'keyword', - }, - }, - type: 'object', - }, - }, - }, - user: { - properties: { - name: { - type: 'keyword', - }, - risk: { - properties: { - calculated_level: { - type: 'keyword', - }, - calculated_score: { - type: 'float', - }, - calculated_score_norm: { - type: 'float', - }, - category_1_count: { - type: 'long', - }, - category_1_score: { - type: 'float', - }, - id_field: { - type: 'keyword', - }, - id_value: { - type: 'keyword', - }, - inputs: { - properties: { - category: { - type: 'keyword', - }, - description: { - type: 'keyword', - }, - id: { - type: 'keyword', - }, - index: { - type: 'keyword', - }, - risk_score: { - type: 'float', - }, - timestamp: { - type: 'date', - }, - }, - type: 'object', - }, - notes: { - type: 'keyword', - }, - }, - type: 'object', - }, - }, - }, - }, - }, - }, - }); - - expect(transforms.createTransform).toHaveBeenCalledWith({ - logger, - esClient, - transform: { - dest: { - index: 'risk-score.risk-score-latest-default', - }, - frequency: '1h', - latest: { - sort: '@timestamp', - unique_key: ['host.name', 'user.name'], - }, - source: { - index: ['risk-score.risk-score-default'], - }, - sync: { - time: { - delay: '2s', - field: '@timestamp', - }, - }, - transform_id: 'risk_score_latest_transform_default', - }, - }); - }); - }); - - describe('initializeResources error', () => { - it('should handle errors during initialization', async () => { - const error = new Error('There error'); - (createOrUpdateIndexTemplate as jest.Mock).mockRejectedValueOnce(error); - - try { - await riskEngineDataClient.initializeResources({ namespace: 'default' }); - } catch (e) { - expect(logger.error).toHaveBeenCalledWith( - `Error initializing risk engine resources: ${error.message}` - ); - } - }); - }); - - describe('getStatus', () => { - it('should return initial status', async () => { - const status = await riskEngineDataClient.getStatus({ - namespace: 'default', - }); - expect(status).toEqual({ - isMaxAmountOfRiskEnginesReached: false, - riskEngineStatus: 'NOT_INSTALLED', - legacyRiskEngineStatus: 'NOT_INSTALLED', - }); - }); - - describe('saved object exists and transforms not', () => { - beforeEach(() => { - mockSavedObjectClient.find.mockResolvedValue(getSavedObjectConfiguration()); - }); - - it('should return status with enabled true', async () => { - mockSavedObjectClient.find.mockResolvedValue( - getSavedObjectConfiguration({ - enabled: true, - }) - ); - - const status = await riskEngineDataClient.getStatus({ - namespace: 'default', - }); - expect(status).toEqual({ - isMaxAmountOfRiskEnginesReached: true, - riskEngineStatus: 'ENABLED', - legacyRiskEngineStatus: 'NOT_INSTALLED', - }); - }); - - it('should return status with enabled false', async () => { - mockSavedObjectClient.find.mockResolvedValue(getSavedObjectConfiguration()); - - const status = await riskEngineDataClient.getStatus({ - namespace: 'default', - }); - expect(status).toEqual({ - isMaxAmountOfRiskEnginesReached: false, - riskEngineStatus: 'DISABLED', - legacyRiskEngineStatus: 'NOT_INSTALLED', - }); - }); - }); - - describe('legacy transforms', () => { - it('should fetch transforms', async () => { - await riskEngineDataClient.getStatus({ - namespace: 'default', - }); - - expect(esClient.transform.getTransform).toHaveBeenCalledTimes(4); - expect(esClient.transform.getTransform).toHaveBeenNthCalledWith(1, { - transform_id: 'ml_hostriskscore_pivot_transform_default', - }); - expect(esClient.transform.getTransform).toHaveBeenNthCalledWith(2, { - transform_id: 'ml_hostriskscore_latest_transform_default', - }); - expect(esClient.transform.getTransform).toHaveBeenNthCalledWith(3, { - transform_id: 'ml_userriskscore_pivot_transform_default', - }); - expect(esClient.transform.getTransform).toHaveBeenNthCalledWith(4, { - transform_id: 'ml_userriskscore_latest_transform_default', - }); - }); - - it('should return that legacy transform enabled if at least on transform exist', async () => { - esClient.transform.getTransform.mockResolvedValueOnce(transformsMock); - - const status = await riskEngineDataClient.getStatus({ - namespace: 'default', - }); - - expect(status).toEqual({ - isMaxAmountOfRiskEnginesReached: false, - riskEngineStatus: 'NOT_INSTALLED', - legacyRiskEngineStatus: 'ENABLED', - }); - - esClient.transform.getTransformStats.mockReset(); - }); - }); - }); - describe('#getConfiguration', () => { it('retrieves configuration from the saved object', async () => { mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); @@ -703,10 +205,7 @@ describe('RiskEngineDataClient', () => { describe('init', () => { let mockTaskManagerStart: ReturnType; - const initializeResourcesMock = jest.spyOn( - RiskEngineDataClient.prototype, - 'initializeResources' - ); + const initRiskScore = jest.spyOn(RiskScoreDataClient.prototype, 'init'); const enableRiskEngineMock = jest.spyOn(RiskEngineDataClient.prototype, 'enableRiskEngine'); const disableLegacyRiskEngineMock = jest.spyOn( @@ -717,7 +216,7 @@ describe('RiskEngineDataClient', () => { mockTaskManagerStart = taskManagerMock.createStart(); disableLegacyRiskEngineMock.mockImplementation(() => Promise.resolve(true)); - initializeResourcesMock.mockImplementation(() => { + initRiskScore.mockImplementation(() => { return Promise.resolve(); }); @@ -731,7 +230,7 @@ describe('RiskEngineDataClient', () => { }); afterEach(() => { - initializeResourcesMock.mockReset(); + initRiskScore.mockReset(); enableRiskEngineMock.mockReset(); disableLegacyRiskEngineMock.mockReset(); }); @@ -740,6 +239,7 @@ describe('RiskEngineDataClient', () => { const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient: riskScoreDataClientMock.create(), }); expect(initResult).toEqual({ @@ -758,6 +258,7 @@ describe('RiskEngineDataClient', () => { const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient: riskScoreDataClientMock.create(), }); expect(initResult).toEqual({ @@ -777,6 +278,7 @@ describe('RiskEngineDataClient', () => { const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient: riskScoreDataClientMock.create(), }); expect(initResult).toEqual({ @@ -789,17 +291,19 @@ describe('RiskEngineDataClient', () => { }); it('should catch error for initializeResources and stop', async () => { - initializeResourcesMock.mockImplementationOnce(() => { - throw new Error('Error initializeResourcesMock'); + const riskScoreDataClient = riskScoreDataClientMock.create(); + riskScoreDataClient.init.mockImplementationOnce(() => { + throw new Error('Error riskScoreDataClient'); }); const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient, }); expect(initResult).toEqual({ - errors: ['Error initializeResourcesMock'], + errors: ['Error riskScoreDataClient'], legacyRiskEngineDisabled: true, riskEngineConfigurationCreated: false, riskEngineEnabled: false, @@ -815,6 +319,7 @@ describe('RiskEngineDataClient', () => { const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient: riskScoreDataClientMock.create(), }); expect(initResult).toEqual({ @@ -834,6 +339,7 @@ describe('RiskEngineDataClient', () => { const initResult = await riskEngineDataClient.init({ namespace: 'default', taskManager: mockTaskManagerStart, + riskScoreDataClient: riskScoreDataClientMock.create(), }); expect(initResult).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts index ea564ffe2395c..e649fa24b2139 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts @@ -5,58 +5,29 @@ * 2.0. */ -import type { Metadata } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - createOrUpdateComponentTemplate, - createOrUpdateIndexTemplate, -} from '@kbn/alerting-plugin/server'; -import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; -import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; - -import { - riskScoreFieldMap, - getIndexPatternDataStream, - totalFieldsLimit, - mappingComponentName, - getTransformOptions, -} from './configurations'; -import { createDataStream } from './utils/create_datastream'; -import type { RiskEngineDataWriter as Writer } from './risk_engine_data_writer'; -import { RiskEngineDataWriter } from './risk_engine_data_writer'; import type { InitRiskEngineResult } from '../../../../common/risk_engine'; import { RiskEngineStatus, - getRiskScoreLatestIndex, MAX_SPACES_COUNT, RiskScoreEntity, } from '../../../../common/risk_engine'; -import { - getLegacyTransforms, - getLatestTransformId, - removeLegacyTransforms, - createTransform, -} from './utils/transforms'; +import { removeLegacyTransforms, getLegacyTransforms } from '../utils/transforms'; import { updateSavedObjectAttribute, getConfiguration, initSavedObjects, getEnabledRiskEngineAmount, } from './utils/saved_object_configuration'; -import { getRiskInputsIndex } from './get_risk_inputs_index'; -import { removeRiskScoringTask, startRiskScoringTask } from './tasks'; -import { createIndex } from './utils/create_index'; import { bulkDeleteSavedObjects } from '../../risk_score/prebuilt_saved_objects/helpers/bulk_delete_saved_objects'; +import type { RiskScoreDataClient } from '../risk_score/risk_score_data_client'; +import { removeRiskScoringTask, startRiskScoringTask } from '../risk_score/tasks'; interface InitOpts { namespace: string; taskManager: TaskManagerStartContract; -} - -interface InitializeRiskEngineResourcesOpts { - namespace?: string; + riskScoreDataClient: RiskScoreDataClient; } interface RiskEngineDataClientOpts { @@ -68,10 +39,9 @@ interface RiskEngineDataClientOpts { } export class RiskEngineDataClient { - private writerCache: Map = new Map(); constructor(private readonly options: RiskEngineDataClientOpts) {} - public async init({ namespace, taskManager }: InitOpts) { + public async init({ namespace, taskManager, riskScoreDataClient }: InitOpts) { const result: InitRiskEngineResult = { legacyRiskEngineDisabled: false, riskEngineResourcesInstalled: false, @@ -88,7 +58,7 @@ export class RiskEngineDataClient { } try { - await this.initializeResources({ namespace }); + await riskScoreDataClient.init(); result.riskEngineResourcesInstalled = true; } catch (e) { result.errors.push(e.message); @@ -106,6 +76,7 @@ export class RiskEngineDataClient { return result; } + // should be the last step, after all resources are installed try { await this.enableRiskEngine({ taskManager }); result.riskEngineEnabled = true; @@ -117,39 +88,11 @@ export class RiskEngineDataClient { return result; } - public async getWriter({ namespace }: { namespace: string }): Promise { - if (this.writerCache.get(namespace)) { - return this.writerCache.get(namespace) as Writer; - } - const indexPatterns = getIndexPatternDataStream(namespace); - await this.initializeWriter(namespace, indexPatterns.alias); - return this.writerCache.get(namespace) as Writer; - } - - private async initializeWriter(namespace: string, index: string): Promise { - const writer = new RiskEngineDataWriter({ - esClient: this.options.esClient, - namespace, - index, - logger: this.options.logger, - }); - - this.writerCache.set(namespace, writer); - return writer; - } - public getConfiguration = () => getConfiguration({ savedObjectsClient: this.options.soClient, }); - public getRiskInputsIndex = ({ dataViewId }: { dataViewId: string }) => - getRiskInputsIndex({ - dataViewId, - logger: this.options.logger, - soClient: this.options.soClient, - }); - public async getStatus({ namespace }: { namespace: string }) { const riskEngineStatus = await this.getCurrentStatus(); const legacyRiskEngineStatus = await this.getLegacyStatus({ namespace }); @@ -258,96 +201,4 @@ export class RiskEngineDataClient { return RiskEngineStatus.ENABLED; } - - public async initializeResources({ - namespace = DEFAULT_NAMESPACE_STRING, - }: InitializeRiskEngineResourcesOpts) { - try { - const esClient = this.options.esClient; - - const indexPatterns = getIndexPatternDataStream(namespace); - - const indexMetadata: Metadata = { - kibana: { - version: this.options.kibanaVersion, - }, - managed: true, - namespace, - }; - - await Promise.all([ - createOrUpdateComponentTemplate({ - logger: this.options.logger, - esClient, - template: { - name: mappingComponentName, - _meta: { - managed: true, - }, - template: { - settings: {}, - mappings: mappingFromFieldMap(riskScoreFieldMap, 'strict'), - }, - } as ClusterPutComponentTemplateRequest, - totalFieldsLimit, - }), - ]); - - await createOrUpdateIndexTemplate({ - logger: this.options.logger, - esClient, - template: { - name: indexPatterns.template, - body: { - data_stream: { hidden: true }, - index_patterns: [indexPatterns.alias], - composed_of: [mappingComponentName], - template: { - lifecycle: {}, - settings: { - 'index.mapping.total_fields.limit': totalFieldsLimit, - }, - mappings: { - dynamic: false, - _meta: indexMetadata, - }, - }, - _meta: indexMetadata, - }, - }, - }); - - await createDataStream({ - logger: this.options.logger, - esClient, - totalFieldsLimit, - indexPatterns, - }); - - await createIndex({ - esClient, - logger: this.options.logger, - options: { - index: getRiskScoreLatestIndex(namespace), - mappings: mappingFromFieldMap(riskScoreFieldMap, 'strict'), - }, - }); - - const transformId = getLatestTransformId(namespace); - await createTransform({ - esClient, - logger: this.options.logger, - transform: { - transform_id: transformId, - ...getTransformOptions({ - dest: getRiskScoreLatestIndex(namespace), - source: [indexPatterns.alias], - }), - }, - }); - } catch (error) { - this.options.logger.error(`Error initializing risk engine resources: ${error.message}`); - throw error; - } - } } diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts index 23e58896199a9..eb7ae66c2c3d3 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts @@ -6,7 +6,7 @@ */ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; -import { riskEngineDisableRoute } from './risk_engine_disable_route'; +import { riskEngineDisableRoute } from './disable'; import { RISK_ENGINE_DISABLE_URL } from '../../../../../common/constants'; import { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_disable_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts index 79a6c88c4fadf..02ccf01dc6166 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts @@ -6,7 +6,7 @@ */ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; -import { riskEngineEnableRoute } from './risk_engine_enable_route'; +import { riskEngineEnableRoute } from './enable'; import { RISK_ENGINE_ENABLE_URL } from '../../../../../common/constants'; import { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_enable_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts index 1c37efc508f05..7e16ed57946e5 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -export { riskScorePreviewRoute } from './risk_score_preview_route'; -export { riskEngineInitRoute } from './risk_engine_init_route'; -export { riskEngineEnableRoute } from './risk_engine_enable_route'; -export { riskEngineDisableRoute } from './risk_engine_disable_route'; -export { riskEngineStatusRoute } from './risk_engine_status_route'; +export { riskEngineInitRoute } from './init'; +export { riskEngineEnableRoute } from './enable'; +export { riskEngineDisableRoute } from './disable'; +export { riskEngineStatusRoute } from './status'; +export { riskEnginePrivilegesRoute } from './privileges'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_init_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts similarity index 92% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_init_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts index e5b719dfbc42f..a76af277949fd 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_init_route.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts @@ -12,6 +12,7 @@ import { RISK_ENGINE_INIT_URL, APP_ID } from '../../../../../common/constants'; import type { StartPlugins } from '../../../../plugin'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import type { SecuritySolutionPluginRouter } from '../../../../types'; +import type { InitRiskEngineResultResponse } from '../../types'; export const riskEngineInitRoute = ( router: SecuritySolutionPluginRouter, @@ -30,6 +31,7 @@ export const riskEngineInitRoute = ( const securitySolution = await context.securitySolution; const [_, { taskManager }] = await getStartServices(); const riskEngineDataClient = securitySolution.getRiskEngineDataClient(); + const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); const spaceId = securitySolution.getSpaceId(); try { @@ -43,9 +45,10 @@ export const riskEngineInitRoute = ( const initResult = await riskEngineDataClient.init({ taskManager, namespace: spaceId, + riskScoreDataClient, }); - const initResultResponse = { + const initResultResponse: InitRiskEngineResultResponse = { risk_engine_enabled: initResult.riskEngineEnabled, risk_engine_resources_installed: initResult.riskEngineResourcesInstalled, risk_engine_configuration_created: initResult.riskEngineConfigurationCreated, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts new file mode 100644 index 0000000000000..d035119ec0f1e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { StartServicesAccessor } from '@kbn/core/server'; +import { RISK_ENGINE_PRIVILEGES_URL, APP_ID } from '../../../../../common/constants'; + +import type { StartPlugins } from '../../../../plugin'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { getUserRiskEnginePrivileges } from '../get_user_risk_engine_privileges'; + +export const riskEnginePrivilegesRoute = ( + router: SecuritySolutionPluginRouter, + getStartServices: StartServicesAccessor +) => { + router.versioned + .get({ + access: 'internal', + path: RISK_ENGINE_PRIVILEGES_URL, + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion({ version: '1', validate: false }, async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + const [_, { security }] = await getStartServices(); + const body = await getUserRiskEnginePrivileges(request, security); + + try { + return response.ok({ + body, + }); + } catch (e) { + const error = transformError(e); + + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_status_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_status_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml index c51bf19ebd2b1..d9840840ea2f6 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml @@ -93,6 +93,16 @@ paths: application/json: schema: $ref: '#/components/schemas/RiskEngineDisableResponse' + /engine/privileges: + get: + summary: Check if the user has access to the risk engine + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/RiskEnginePrivilegesResponse' components: @@ -436,4 +446,25 @@ components: type: boolean error: type: string - \ No newline at end of file + RiskEnginePrivilegesResponse: + type: object + properties: + privileges: + type: object + properties: + elasticsearch: + type: object + properties: + cluster: + type: object + additionalProperties: + type: boolean + index: + type: object + additionalProperties: + type: object + additionalProperties: + type: boolean + has_all_required: + description: If true then the user has full access to the risk engine + type: boolean diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/saved_object_configuration.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/saved_object_configuration.ts index e39f2f73e5df2..d5a6451cb207b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/saved_object_configuration.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/saved_object_configuration.ts @@ -7,7 +7,7 @@ import type { SavedObject, SavedObjectsClientContract } from '@kbn/core/server'; import { getAlertsIndex } from '../../../../../common/utils/risk_score_modules'; -import type { RiskEngineConfiguration } from '../types'; +import type { RiskEngineConfiguration } from '../../types'; import { riskEngineConfigurationTypeName } from '../saved_object'; export interface SavedObjectsClientArg { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts similarity index 89% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.mock.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts index e76c9a5a79556..093e1b8235113 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { CalculateAndPersistScoresResponse } from './types'; +import type { CalculateAndPersistScoresResponse } from '../types'; const buildResponseMock = ( overrides: Partial = {} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.test.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.test.ts index cc9a51a6d53c5..1aa38395baf17 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.test.ts @@ -41,7 +41,7 @@ describe('calculateAndPersistRiskScores', () => { range: { start: 'now - 15d', end: 'now' }, spaceId: 'default', // @ts-expect-error not relevant for this test - riskEngineDataClient: { getWriter: jest.fn() }, + riskScoreDataClient: { getWriter: jest.fn() }, runtimeMappings: {}, }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts similarity index 79% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts index ba5b4fc7d5cfe..77258d313034c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_and_persist_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts @@ -7,8 +7,8 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; -import type { RiskEngineDataClient } from './risk_engine_data_client'; -import type { CalculateAndPersistScoresParams, CalculateAndPersistScoresResponse } from './types'; +import type { RiskScoreDataClient } from './risk_score_data_client'; +import type { CalculateAndPersistScoresParams, CalculateAndPersistScoresResponse } from '../types'; import { calculateRiskScores } from './calculate_risk_scores'; export const calculateAndPersistRiskScores = async ( @@ -16,11 +16,11 @@ export const calculateAndPersistRiskScores = async ( esClient: ElasticsearchClient; logger: Logger; spaceId: string; - riskEngineDataClient: RiskEngineDataClient; + riskScoreDataClient: RiskScoreDataClient; } ): Promise => { - const { riskEngineDataClient, spaceId, ...rest } = params; - const writer = await riskEngineDataClient.getWriter({ + const { riskScoreDataClient, spaceId, ...rest } = params; + const writer = await riskScoreDataClient.getWriter({ namespace: spaceId, }); const { after_keys: afterKeys, scores } = await calculateRiskScores(rest); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.mock.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts index c81d1336c162b..4b0d298ca4201 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts @@ -14,7 +14,7 @@ import type { CalculateRiskScoreAggregations, CalculateScoresResponse, RiskScoreBucket, -} from './types'; +} from '../types'; const buildRiskScoreBucketMock = (overrides: Partial = {}): RiskScoreBucket => ({ key: { 'user.name': 'username' }, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index ecacf95020251..996adec9c4908 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -38,7 +38,7 @@ import type { CalculateScoresParams, CalculateScoresResponse, RiskScoreBucket, -} from './types'; +} from '../types'; const bucketToResponse = ({ bucket, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/configurations.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/configurations.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts index 35547187e4ddc..4ed0533292246 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/configurations.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts @@ -7,7 +7,7 @@ import type { FieldMap } from '@kbn/alerts-as-data-utils'; import type { IdentifierType } from '../../../../common/risk_engine'; import { RiskScoreEntity, riskScoreBaseIndexName } from '../../../../common/risk_engine'; -import type { IIndexPatternString } from './utils/create_datastream'; +import type { IIndexPatternString } from '../utils/create_datastream'; const commonRiskFields: FieldMap = { id_field: { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.mock.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.mock.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.mock.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_risk_inputs_index.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/get_risk_inputs_index.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/helpers.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/helpers.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts similarity index 93% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/helpers.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts index 09836ff94fe2d..617b2c5a03c10 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts @@ -6,7 +6,7 @@ */ import type { AfterKey, AfterKeys, IdentifierType } from '../../../../common/risk_engine'; -import type { CalculateAndPersistScoresResponse } from './types'; +import type { CalculateAndPersistScoresResponse } from '../types'; export const getFieldForIdentifierAgg = (identifierType: IdentifierType): string => identifierType === 'host' ? 'host.name' : 'user.name'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_writer.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_writer.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_writer.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_writer.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.mock.ts new file mode 100644 index 0000000000000..0277e9fc91a0f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RiskScoreDataClient } from './risk_score_data_client'; + +const createRiskScoreDataClientMock = () => + ({ + getWriter: jest.fn(), + init: jest.fn(), + getRiskInputsIndex: jest.fn(), + } as unknown as jest.Mocked); + +export const riskScoreDataClientMock = { create: createRiskScoreDataClientMock }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts new file mode 100644 index 0000000000000..022d8e92d4577 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.test.ts @@ -0,0 +1,455 @@ +/* + * Copyright 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 { + createOrUpdateComponentTemplate, + createOrUpdateIndexTemplate, +} from '@kbn/alerting-plugin/server'; +import { + loggingSystemMock, + elasticsearchServiceMock, + savedObjectsClientMock, +} from '@kbn/core/server/mocks'; + +import { RiskScoreDataClient } from './risk_score_data_client'; + +import { createDataStream } from '../utils/create_datastream'; + +import * as transforms from '../utils/transforms'; +import { createOrUpdateIndex } from '../utils/create_or_update_index'; + +jest.mock('@kbn/alerting-plugin/server', () => ({ + createOrUpdateComponentTemplate: jest.fn(), + createOrUpdateIndexTemplate: jest.fn(), +})); + +jest.mock('../utils/create_datastream', () => ({ + createDataStream: jest.fn(), +})); + +jest.mock('../utils/create_or_update_index', () => ({ + createOrUpdateIndex: jest.fn(), +})); + +jest.spyOn(transforms, 'createTransform').mockResolvedValue(Promise.resolve()); +jest.spyOn(transforms, 'startTransform').mockResolvedValue(Promise.resolve()); + +describe('RiskScoreDataClient', () => { + let riskScoreDataClient: RiskScoreDataClient; + let mockSavedObjectClient: ReturnType; + let logger: ReturnType; + const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; + const totalFieldsLimit = 1000; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + mockSavedObjectClient = savedObjectsClientMock.create(); + const options = { + logger, + kibanaVersion: '8.9.0', + esClient, + soClient: mockSavedObjectClient, + namespace: 'default', + }; + riskScoreDataClient = new RiskScoreDataClient(options); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getWriter', () => { + it('should return a writer object', async () => { + const writer = await riskScoreDataClient.getWriter({ namespace: 'default' }); + expect(writer).toBeDefined(); + expect(typeof writer?.bulk).toBe('function'); + }); + + it('should cache and return the same writer for the same namespace', async () => { + const writer1 = await riskScoreDataClient.getWriter({ namespace: 'default' }); + const writer2 = await riskScoreDataClient.getWriter({ namespace: 'default' }); + const writer3 = await riskScoreDataClient.getWriter({ namespace: 'space-1' }); + + expect(writer1).toEqual(writer2); + expect(writer2).not.toEqual(writer3); + }); + }); + + describe('init success', () => { + it('should initialize risk engine resources', async () => { + await riskScoreDataClient.init(); + + expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith( + expect.objectContaining({ + logger, + esClient, + template: expect.objectContaining({ + name: '.risk-score-mappings', + _meta: { + managed: true, + }, + }), + totalFieldsLimit: 1000, + }) + ); + expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template) + .toMatchInlineSnapshot(` + Object { + "mappings": Object { + "dynamic": "strict", + "properties": Object { + "@timestamp": Object { + "ignore_malformed": false, + "type": "date", + }, + "host": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, + }, + "type": "object", + }, + "notes": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + }, + }, + "user": Object { + "properties": Object { + "name": Object { + "type": "keyword", + }, + "risk": Object { + "properties": Object { + "calculated_level": Object { + "type": "keyword", + }, + "calculated_score": Object { + "type": "float", + }, + "calculated_score_norm": Object { + "type": "float", + }, + "category_1_count": Object { + "type": "long", + }, + "category_1_score": Object { + "type": "float", + }, + "id_field": Object { + "type": "keyword", + }, + "id_value": Object { + "type": "keyword", + }, + "inputs": Object { + "properties": Object { + "category": Object { + "type": "keyword", + }, + "description": Object { + "type": "keyword", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "risk_score": Object { + "type": "float", + }, + "timestamp": Object { + "type": "date", + }, + }, + "type": "object", + }, + "notes": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + }, + }, + }, + }, + "settings": Object {}, + } + `); + + expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({ + logger, + esClient, + template: { + name: '.risk-score.risk-score-default-index-template', + body: { + data_stream: { hidden: true }, + index_patterns: ['risk-score.risk-score-default'], + composed_of: ['.risk-score-mappings'], + template: { + lifecycle: {}, + settings: { + 'index.mapping.total_fields.limit': totalFieldsLimit, + }, + mappings: { + dynamic: false, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace: 'default', + }, + }, + }, + _meta: { + kibana: { + version: '8.9.0', + }, + managed: true, + namespace: 'default', + }, + }, + }, + }); + + expect(createDataStream).toHaveBeenCalledWith({ + logger, + esClient, + totalFieldsLimit, + indexPatterns: { + template: `.risk-score.risk-score-default-index-template`, + alias: `risk-score.risk-score-default`, + }, + }); + + expect(createOrUpdateIndex).toHaveBeenCalledWith({ + logger, + esClient, + options: { + index: `risk-score.risk-score-latest-default`, + mappings: { + dynamic: 'strict', + properties: { + '@timestamp': { + ignore_malformed: false, + type: 'date', + }, + host: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', + }, + }, + type: 'object', + }, + notes: { + type: 'keyword', + }, + }, + type: 'object', + }, + }, + }, + user: { + properties: { + name: { + type: 'keyword', + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + category_1_count: { + type: 'long', + }, + category_1_score: { + type: 'float', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + inputs: { + properties: { + category: { + type: 'keyword', + }, + description: { + type: 'keyword', + }, + id: { + type: 'keyword', + }, + index: { + type: 'keyword', + }, + risk_score: { + type: 'float', + }, + timestamp: { + type: 'date', + }, + }, + type: 'object', + }, + notes: { + type: 'keyword', + }, + }, + type: 'object', + }, + }, + }, + }, + }, + }, + }); + + expect(transforms.createTransform).toHaveBeenCalledWith({ + logger, + esClient, + transform: { + dest: { + index: 'risk-score.risk-score-latest-default', + }, + frequency: '1h', + latest: { + sort: '@timestamp', + unique_key: ['host.name', 'user.name'], + }, + source: { + index: ['risk-score.risk-score-default'], + }, + sync: { + time: { + delay: '2s', + field: '@timestamp', + }, + }, + transform_id: 'risk_score_latest_transform_default', + }, + }); + }); + }); + + describe('init error', () => { + it('should handle errors during initialization', async () => { + const error = new Error('There error'); + (createOrUpdateIndexTemplate as jest.Mock).mockRejectedValueOnce(error); + + try { + await riskScoreDataClient.init(); + } catch (e) { + expect(logger.error).toHaveBeenCalledWith( + `Error initializing risk engine resources: ${error.message}` + ); + } + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts new file mode 100644 index 0000000000000..0b8cea72d25c2 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -0,0 +1,164 @@ +/* + * Copyright 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 { Metadata } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { + createOrUpdateComponentTemplate, + createOrUpdateIndexTemplate, +} from '@kbn/alerting-plugin/server'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; +import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; + +import { + riskScoreFieldMap, + getIndexPatternDataStream, + totalFieldsLimit, + mappingComponentName, + getTransformOptions, +} from './configurations'; +import { createDataStream } from '../utils/create_datastream'; +import type { RiskEngineDataWriter as Writer } from './risk_engine_data_writer'; +import { RiskEngineDataWriter } from './risk_engine_data_writer'; +import { getRiskScoreLatestIndex } from '../../../../common/risk_engine'; +import { getLatestTransformId, createTransform } from '../utils/transforms'; +import { getRiskInputsIndex } from './get_risk_inputs_index'; + +import { createOrUpdateIndex } from '../utils/create_or_update_index'; + +interface RiskScoringDataClientOpts { + logger: Logger; + kibanaVersion: string; + esClient: ElasticsearchClient; + namespace: string; + soClient: SavedObjectsClientContract; +} + +export class RiskScoreDataClient { + private writerCache: Map = new Map(); + constructor(private readonly options: RiskScoringDataClientOpts) {} + + public async getWriter({ namespace }: { namespace: string }): Promise { + if (this.writerCache.get(namespace)) { + return this.writerCache.get(namespace) as Writer; + } + const indexPatterns = getIndexPatternDataStream(namespace); + await this.initializeWriter(namespace, indexPatterns.alias); + return this.writerCache.get(namespace) as Writer; + } + + private async initializeWriter(namespace: string, index: string): Promise { + const writer = new RiskEngineDataWriter({ + esClient: this.options.esClient, + namespace, + index, + logger: this.options.logger, + }); + + this.writerCache.set(namespace, writer); + return writer; + } + + public getRiskInputsIndex = ({ dataViewId }: { dataViewId: string }) => + getRiskInputsIndex({ + dataViewId, + logger: this.options.logger, + soClient: this.options.soClient, + }); + + public async init() { + const namespace = this.options.namespace; + + try { + const esClient = this.options.esClient; + + const indexPatterns = getIndexPatternDataStream(namespace); + + const indexMetadata: Metadata = { + kibana: { + version: this.options.kibanaVersion, + }, + managed: true, + namespace, + }; + + await Promise.all([ + createOrUpdateComponentTemplate({ + logger: this.options.logger, + esClient, + template: { + name: mappingComponentName, + _meta: { + managed: true, + }, + template: { + settings: {}, + mappings: mappingFromFieldMap(riskScoreFieldMap, 'strict'), + }, + } as ClusterPutComponentTemplateRequest, + totalFieldsLimit, + }), + ]); + + await createOrUpdateIndexTemplate({ + logger: this.options.logger, + esClient, + template: { + name: indexPatterns.template, + body: { + data_stream: { hidden: true }, + index_patterns: [indexPatterns.alias], + composed_of: [mappingComponentName], + template: { + lifecycle: {}, + settings: { + 'index.mapping.total_fields.limit': totalFieldsLimit, + }, + mappings: { + dynamic: false, + _meta: indexMetadata, + }, + }, + _meta: indexMetadata, + }, + }, + }); + + await createDataStream({ + logger: this.options.logger, + esClient, + totalFieldsLimit, + indexPatterns, + }); + + await createOrUpdateIndex({ + esClient, + logger: this.options.logger, + options: { + index: getRiskScoreLatestIndex(namespace), + mappings: mappingFromFieldMap(riskScoreFieldMap, 'strict'), + }, + }); + + const transformId = getLatestTransformId(namespace); + await createTransform({ + esClient, + logger: this.options.logger, + transform: { + transform_id: transformId, + ...getTransformOptions({ + dest: getRiskScoreLatestIndex(namespace), + source: [indexPatterns.alias], + }), + }, + }); + } catch (error) { + this.options.logger.error(`Error initializing risk engine resources: ${error.message}`); + throw error; + } + } +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_score_service.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_score_service.mock.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_score_service.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts similarity index 81% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_score_service.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts index ecc731696f146..a89835c6c7327 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_score_service.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts @@ -12,12 +12,13 @@ import type { CalculateScoresParams, CalculateScoresResponse, RiskEngineConfiguration, -} from './types'; +} from '../types'; import { calculateRiskScores } from './calculate_risk_scores'; import { calculateAndPersistRiskScores } from './calculate_and_persist_risk_scores'; -import type { RiskEngineDataClient } from './risk_engine_data_client'; +import type { RiskEngineDataClient } from '../risk_engine/risk_engine_data_client'; +import type { RiskScoreDataClient } from './risk_score_data_client'; import type { RiskInputsIndexResponse } from './get_risk_inputs_index'; -import { scheduleLatestTransformNow } from './utils/transforms'; +import { scheduleLatestTransformNow } from '../utils/transforms'; export interface RiskScoreService { calculateScores: (params: CalculateScoresParams) => Promise; @@ -33,6 +34,7 @@ export interface RiskScoreServiceFactoryParams { esClient: ElasticsearchClient; logger: Logger; riskEngineDataClient: RiskEngineDataClient; + riskScoreDataClient: RiskScoreDataClient; spaceId: string; } @@ -40,12 +42,13 @@ export const riskScoreServiceFactory = ({ esClient, logger, riskEngineDataClient, + riskScoreDataClient, spaceId, }: RiskScoreServiceFactoryParams): RiskScoreService => ({ calculateScores: (params) => calculateRiskScores({ ...params, esClient, logger }), calculateAndPersistScores: (params) => - calculateAndPersistRiskScores({ ...params, esClient, logger, riskEngineDataClient, spaceId }), + calculateAndPersistRiskScores({ ...params, esClient, logger, riskScoreDataClient, spaceId }), getConfiguration: async () => riskEngineDataClient.getConfiguration(), - getRiskInputsIndex: async (params) => riskEngineDataClient.getRiskInputsIndex(params), + getRiskInputsIndex: async (params) => riskScoreDataClient.getRiskInputsIndex(params), scheduleLatestTransformNow: () => scheduleLatestTransformNow({ namespace: spaceId, esClient }), }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_weights.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_weights.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_weights.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts similarity index 98% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts index 7cf7b5304a01d..0a62695dfd680 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { riskScoreCalculationRoute } from './risk_score_calculation_route'; +import { riskScoreCalculationRoute } from './calculation'; import { loggerMock } from '@kbn/logging-mocks'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts index cfbd80afb0d5f..bb77d999aef4b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_calculation_route.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts @@ -41,11 +41,13 @@ export const riskScoreCalculationRoute = (router: SecuritySolutionPluginRouter, const soClient = coreContext.savedObjects.client; const spaceId = securityContext.getSpaceId(); const riskEngineDataClient = securityContext.getRiskEngineDataClient(); + const riskScoreDataClient = securityContext.getRiskScoreDataClient(); const riskScoreService = riskScoreServiceFactory({ esClient, logger, riskEngineDataClient, + riskScoreDataClient, spaceId, }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts new file mode 100644 index 0000000000000..56d769899641d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { riskScorePreviewRoute } from './preview'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index ba87e94c3ccc2..09f6335077c86 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -17,7 +17,7 @@ import { import { getRiskInputsIndex } from '../get_risk_inputs_index'; import { riskScoreServiceFactory } from '../risk_score_service'; import { riskScoreServiceMock } from '../risk_score_service.mock'; -import { riskScorePreviewRoute } from './risk_score_preview_route'; +import { riskScorePreviewRoute } from './preview'; jest.mock('../risk_score_service'); jest.mock('../get_risk_inputs_index'); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts index 05f80c526a927..eaf54cf5e5dea 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_score_preview_route.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts @@ -42,11 +42,13 @@ export const riskScorePreviewRoute = (router: SecuritySolutionPluginRouter, logg const soClient = coreContext.savedObjects.client; const spaceId = securityContext.getSpaceId(); const riskEngineDataClient = securityContext.getRiskEngineDataClient(); + const riskScoreDataClient = securityContext.getRiskScoreDataClient(); const riskScoreService = riskScoreServiceFactory({ esClient, logger, riskEngineDataClient, + riskScoreDataClient, spaceId, }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/constants.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/constants.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/constants.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/helpers.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/helpers.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/helpers.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/index.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/index.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.mock.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.mock.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.mock.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts similarity index 99% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts index 627c92dfb6d53..45562ac8d38a9 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts @@ -14,7 +14,7 @@ import type { AnalyticsServiceSetup } from '@kbn/core/public'; import type { RiskScoreService } from '../risk_score_service'; import { riskScoreServiceMock } from '../risk_score_service.mock'; import { riskScoringTaskMock } from './risk_scoring_task.mock'; -import { riskEngineDataClientMock } from '../risk_engine_data_client.mock'; +import { riskEngineDataClientMock } from '../../risk_engine/risk_engine_data_client.mock'; import { registerRiskScoringTask, startRiskScoringTask, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts similarity index 96% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts index 525f2247b63bd..61a733907fb38 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/risk_scoring_task.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts @@ -21,7 +21,8 @@ import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import type { AfterKeys, IdentifierType } from '../../../../../common/risk_engine'; import type { StartPlugins } from '../../../../plugin'; import { type RiskScoreService, riskScoreServiceFactory } from '../risk_score_service'; -import { RiskEngineDataClient } from '../risk_engine_data_client'; +import { RiskEngineDataClient } from '../../risk_engine/risk_engine_data_client'; +import { RiskScoreDataClient } from '../risk_score_data_client'; import { isRiskScoreCalculationComplete } from '../helpers'; import { defaultState, @@ -77,11 +78,19 @@ export const registerRiskScoringTask = ({ namespace, soClient, }); + const riskScoreDataClient = new RiskScoreDataClient({ + logger, + kibanaVersion, + esClient, + namespace, + soClient, + }); return riskScoreServiceFactory({ esClient, logger, riskEngineDataClient, + riskScoreDataClient, spaceId: namespace, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/state.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/state.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/tasks/state.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/state.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts similarity index 90% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/types.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts index f5aeaf4f56428..5968b00c28b1d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/types.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts @@ -14,7 +14,7 @@ import type { Range, RiskEngineStatus, RiskScore, -} from '../../../../common/risk_engine'; +} from '../../../common/risk_engine'; export interface CalculateScoresParams { afterKeys: AfterKeys; @@ -64,7 +64,7 @@ export interface GetRiskEngineStatusResponse { is_max_amount_of_risk_engines_reached: boolean; } -interface InitRiskEngineResultResponse { +export interface InitRiskEngineResultResponse { risk_engine_enabled: boolean; risk_engine_resources_installed: boolean; risk_engine_configuration_created: boolean; @@ -98,6 +98,16 @@ export interface DisableRiskEngineResponse { success: boolean; } +export interface RiskEnginePrivilegesResponse { + privileges: { + elasticsearch: { + cluster: Record; + index: Record>; + }; + }; + has_all_required: boolean; +} + export interface CalculateRiskScoreAggregations { user?: { after_key: AfterKey; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_datastream.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts similarity index 74% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_datastream.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts index fee229fa942f9..3ca4572d945ed 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_datastream.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts @@ -9,9 +9,9 @@ // original function create index instead of datastream, and their have plan to use datastream in the future // so we probably should remove this file and use the original when datastream will be supported +import { get } from 'lodash'; import type { IndicesSimulateIndexTemplateResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Logger, ElasticsearchClient } from '@kbn/core/server'; -import { get } from 'lodash'; import { retryTransientEsErrors } from './retry_transient_es_errors'; export interface IIndexPatternString { @@ -19,33 +19,33 @@ export interface IIndexPatternString { alias: string; } -interface ConcreteIndexInfo { - index: string; - alias: string; - isWriteIndex: boolean; +interface CreateConcreteWriteIndexOpts { + logger: Logger; + esClient: ElasticsearchClient; + totalFieldsLimit: number; + indexPatterns: IIndexPatternString; } interface UpdateIndexMappingsOpts { logger: Logger; esClient: ElasticsearchClient; - totalFieldsLimit: number; - concreteIndices: ConcreteIndexInfo[]; + totalFieldsLimit?: number; + indices: string[]; } interface UpdateIndexOpts { logger: Logger; esClient: ElasticsearchClient; - totalFieldsLimit: number; - concreteIndexInfo: ConcreteIndexInfo; + totalFieldsLimit?: number; + index: string; } const updateTotalFieldLimitSetting = async ({ logger, esClient, totalFieldsLimit, - concreteIndexInfo, + index, }: UpdateIndexOpts) => { - const { index, alias } = concreteIndexInfo; try { await retryTransientEsErrors( () => @@ -57,22 +57,17 @@ const updateTotalFieldLimitSetting = async ({ ); } catch (err) { logger.error( - `Failed to PUT index.mapping.total_fields.limit settings for alias ${alias}: ${err.message}` + `Failed to PUT index.mapping.total_fields.limit settings for index ${index}: ${err.message}` ); throw err; } }; -// This will update the mappings of backing indices but *not* the settings. This +// This will update the mappings of indices but *not* the settings. This // is due to the fact settings can be classed as dynamic and static, and static // updates will fail on an index that isn't closed. New settings *will* be applied as part // of the ILM policy rollovers. More info: https://github.com/elastic/kibana/pull/113389#issuecomment-940152654 -const updateUnderlyingMapping = async ({ - logger, - esClient, - concreteIndexInfo, -}: UpdateIndexOpts) => { - const { index, alias } = concreteIndexInfo; +const updateUnderlyingMapping = async ({ logger, esClient, index }: UpdateIndexOpts) => { let simulatedIndexMapping: IndicesSimulateIndexTemplateResponse; try { simulatedIndexMapping = await retryTransientEsErrors( @@ -81,7 +76,7 @@ const updateUnderlyingMapping = async ({ ); } catch (err) { logger.error( - `Ignored PUT mappings for alias ${alias}; error generating simulated mappings: ${err.message}` + `Ignored PUT mappings for index ${index}; error generating simulated mappings: ${err.message}` ); return; } @@ -89,7 +84,7 @@ const updateUnderlyingMapping = async ({ const simulatedMapping = get(simulatedIndexMapping, ['template', 'mappings']); if (simulatedMapping == null) { - logger.error(`Ignored PUT mappings for alias ${alias}; simulated mappings were empty`); + logger.error(`Ignored PUT mappings for index ${index}; simulated mappings were empty`); return; } @@ -98,44 +93,37 @@ const updateUnderlyingMapping = async ({ () => esClient.indices.putMapping({ index, body: simulatedMapping }), { logger } ); + logger.info(`Update mappings for ${index}`); } catch (err) { - logger.error(`Failed to PUT mapping for alias ${alias}: ${err.message}`); + logger.error(`Failed to PUT mapping for index ${index}: ${err.message}`); throw err; } }; /** * Updates the underlying mapping for any existing concrete indices */ -const updateIndexMappings = async ({ +export const updateIndexMappings = async ({ logger, esClient, totalFieldsLimit, - concreteIndices, + indices, }: UpdateIndexMappingsOpts) => { - logger.debug(`Updating underlying mappings for ${concreteIndices.length} indices.`); - - // Update total field limit setting of found indices - // Other index setting changes are not updated at this time - await Promise.all( - concreteIndices.map((index) => - updateTotalFieldLimitSetting({ logger, esClient, totalFieldsLimit, concreteIndexInfo: index }) - ) - ); + logger.info(`Updating underlying mappings for ${indices.length} indices.`); + + if (totalFieldsLimit) { + // Update total field limit setting of found indices + // Other index setting changes are not updated at this time + await Promise.all( + indices.map((index) => + updateTotalFieldLimitSetting({ logger, esClient, totalFieldsLimit, index }) + ) + ); + } // Update mappings of the found indices. - await Promise.all( - concreteIndices.map((index) => - updateUnderlyingMapping({ logger, esClient, totalFieldsLimit, concreteIndexInfo: index }) - ) - ); + await Promise.all(indices.map((index) => updateUnderlyingMapping({ logger, esClient, index }))); }; -interface CreateConcreteWriteIndexOpts { - logger: Logger; - esClient: ElasticsearchClient; - totalFieldsLimit: number; - indexPatterns: IIndexPatternString; -} /** * Create a data stream */ @@ -148,7 +136,7 @@ export const createDataStream = async ({ logger.info(`Creating data stream - ${indexPatterns.alias}`); // check if a datastream already exists - let dataStreams: ConcreteIndexInfo[] = []; + let dataStreams: string[] = []; try { // Specify both the index pattern for the backing indices and their aliases // The alias prevents the request from finding other namespaces that could match the -* pattern @@ -157,11 +145,7 @@ export const createDataStream = async ({ { logger } ); - dataStreams = response.data_streams.map((dataStream) => ({ - index: dataStream.name, - alias: dataStream.name, - isWriteIndex: true, - })); + dataStreams = response.data_streams.map((dataStream) => dataStream.name); logger.debug( `Found ${dataStreams.length} concrete indices for ${indexPatterns.alias} - ${JSON.stringify( @@ -182,7 +166,7 @@ export const createDataStream = async ({ // if a concrete write datastream already exists, update the underlying mapping if (dataStreams.length > 0) { - await updateIndexMappings({ logger, esClient, totalFieldsLimit, concreteIndices: dataStreams }); + await updateIndexMappings({ logger, esClient, totalFieldsLimit, indices: dataStreams }); } // check if a concrete write datastream already exists diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts similarity index 53% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_index.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts index b1aefedc90cef..f9525e14ac6c4 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/create_index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts @@ -10,8 +10,13 @@ import type { IndicesCreateRequest, IndicesCreateResponse, } from '@elastic/elasticsearch/lib/api/types'; +import { retryTransientEsErrors } from './retry_transient_es_errors'; -export const createIndex = async ({ +/** + * It's check for index existatnce, and create index + * or update existing index mappings + */ +export const createOrUpdateIndex = async ({ esClient, logger, options, @@ -25,11 +30,29 @@ export const createIndex = async ({ index: options.index, }); if (isIndexExist) { + const response = await esClient.indices.get({ + index: options.index, + }); + const indices = Object.keys(response ?? {}); logger.info(`${options.index} already exist`); - return; + if (options.mappings) { + await Promise.all( + indices.map(async (index) => { + try { + await retryTransientEsErrors( + () => esClient.indices.putMapping({ index, body: options.mappings }), + { logger } + ); + logger.info(`Update mappings for ${index}`); + } catch (err) { + logger.error(`Failed to PUT mapping for index ${index}: ${err.message}`); + } + }) + ); + } + } else { + return esClient.indices.create(options); } - - return esClient.indices.create(options); } catch (err) { const error = transformError(err); const fullErrorMessage = `Failed to create index: ${options.index}: ${error.message}`; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/retry_transient_es_errors.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/retry_transient_es_errors.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/retry_transient_es_errors.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/retry_transient_es_errors.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/retry_transient_es_errors.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/retry_transient_es_errors.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/retry_transient_es_errors.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/retry_transient_es_errors.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/transforms.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/transforms.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/transforms.test.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/transforms.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/transforms.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/transforms.ts similarity index 97% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/transforms.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/transforms.ts index b78ea9ccfa644..d1a544233339e 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/utils/transforms.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/transforms.ts @@ -14,11 +14,11 @@ import type { TransformPutTransformRequest, TransformGetTransformStatsTransformStats, } from '@elastic/elasticsearch/lib/api/types'; -import { RiskScoreEntity } from '../../../../../common/search_strategy'; +import { RiskScoreEntity } from '../../../../common/search_strategy'; import { getRiskScorePivotTransformId, getRiskScoreLatestTransformId, -} from '../../../../../common/utils/risk_score_modules'; +} from '../../../../common/utils/risk_score_modules'; export const getLegacyTransforms = async ({ namespace, diff --git a/x-pack/plugins/security_solution/server/lib/framework/types.ts b/x-pack/plugins/security_solution/server/lib/framework/types.ts index 8909bb6160134..ef6df370c03c9 100644 --- a/x-pack/plugins/security_solution/server/lib/framework/types.ts +++ b/x-pack/plugins/security_solution/server/lib/framework/types.ts @@ -6,7 +6,7 @@ */ import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; export const internalFrameworkRequest = Symbol('internalFrameworkRequest'); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts index f5718895fff26..4af8d3a9e435d 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts @@ -9,7 +9,7 @@ import moment from 'moment'; import type { ConcreteTaskInstance } from '@kbn/task-manager-plugin/server'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import type { TelemetryEventsSender } from '../sender'; -import type { TelemetryReceiver } from '../receiver'; +import type { ITelemetryReceiver, TelemetryReceiver } from '../receiver'; import type { SecurityTelemetryTaskConfig } from '../task'; import type { PackagePolicy } from '@kbn/fleet-plugin/common/types/models/package_policy'; import { stubEndpointAlertResponse, stubProcessTree, stubFetchTimelineEvents } from './timeline'; @@ -71,7 +71,7 @@ export const stubLicenseInfo: ESLicense = { export const createMockTelemetryReceiver = ( diagnosticsAlert?: unknown, emptyTimelineTree?: boolean -): jest.Mocked => { +): jest.Mocked => { const processTreeResponse = emptyTimelineTree ? Promise.resolve([]) : Promise.resolve(Promise.resolve(stubProcessTree())); @@ -82,18 +82,18 @@ export const createMockTelemetryReceiver = ( fetchLicenseInfo: jest.fn().mockReturnValue(stubLicenseInfo), copyLicenseFields: jest.fn(), fetchFleetAgents: jest.fn(), + openPointInTime: jest.fn(), + getAlertsIndex: jest.fn().mockReturnValue('alerts-*'), fetchDiagnosticAlerts: jest.fn().mockReturnValue(diagnosticsAlert ?? jest.fn()), fetchEndpointMetrics: jest.fn().mockReturnValue(stubEndpointMetricsResponse), fetchEndpointPolicyResponses: jest.fn(), - fetchPrebuiltRuleAlerts: jest.fn().mockReturnValue(prebuiltRuleAlertsResponse), + fetchPrebuiltRuleAlertsBatch: jest.fn().mockReturnValue(prebuiltRuleAlertsResponse), fetchDetectionRulesPackageVersion: jest.fn(), fetchTrustedApplications: jest.fn(), fetchEndpointList: jest.fn(), fetchDetectionRules: jest.fn().mockReturnValue({ body: null }), fetchEndpointMetadata: jest.fn(), - fetchTimelineEndpointAlerts: jest - .fn() - .mockReturnValue(Promise.resolve(stubEndpointAlertResponse())), + fetchTimelineAlerts: jest.fn().mockReturnValue(Promise.resolve(stubEndpointAlertResponse())), buildProcessTree: jest.fn().mockReturnValue(processTreeResponse), fetchTimelineEvents: jest.fn().mockReturnValue(Promise.resolve(stubFetchTimelineEvents())), fetchValueListMetaData: jest.fn(), diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts index 50e0e0be47cdd..8e50e4590a72f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts @@ -27,6 +27,8 @@ export const INSIGHTS_CHANNEL = 'security-insights-v1'; export const TASK_METRICS_CHANNEL = 'task-metrics'; +export const DEFAULT_DIAGNOSTIC_INDEX = '.logs-endpoint.diagnostic.collection-*' as const; + export const DEFAULT_ADVANCED_POLICY_CONFIG_SETTINGS = { linux: { advanced: { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts index d61a3e9206818..5ccbb0f53a331 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/index.test.ts @@ -279,6 +279,25 @@ describe('Security Telemetry filters', () => { }); }); + it('copies over github integration fields', () => { + const event = { + not_event: 'much data, much wow', + github: { + org: 'elastic', + repo: 'kibana', + team: 'elastic/security-data-analytics', + sensitive: 'i contain sensitive data', + }, + }; + expect(copyAllowlistedFields(prebuiltRuleAllowlistFields, event)).toStrictEqual({ + github: { + org: 'elastic', + repo: 'kibana', + team: 'elastic/security-data-analytics', + }, + }); + }); + it('copies over process/parent fields', () => { const event = { not_event: 'much data, much wow', diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts index c08c66a572245..a7e797227d24b 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filterlists/prebuilt_rules_alerts.ts @@ -350,6 +350,14 @@ export const prebuiltRuleAllowlistFields: AllowlistFields = { original_file_name: true, }, }, + github: { + org: true, + team: true, + repo: true, + category: true, + permission: true, + repository_public: true, + }, // Google/GCP google_workspace: { actor: { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index f5d6bc41ee349..3bd6a0d595a79 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -11,17 +11,25 @@ import type { PackagePolicy } from '@kbn/fleet-plugin/common/types/models/packag import { merge, set } from 'lodash'; import type { Logger } from '@kbn/core/server'; import { sha256 } from 'js-sha256'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { copyAllowlistedFields, filterList } from './filterlists'; -import type { PolicyConfig, PolicyData } from '../../../common/endpoint/types'; +import type { PolicyConfig, PolicyData, SafeEndpointEvent } from '../../../common/endpoint/types'; +import type { ITelemetryReceiver } from './receiver'; import type { - ExceptionListItem, + EnhancedAlertEvent, ESClusterInfo, ESLicense, + ExceptionListItem, + ExtraInfo, ListTemplate, + TaskMetric, TelemetryEvent, + TimeFrame, + TimelineResult, + TimelineTelemetryEvent, ValueListResponse, - TaskMetric, } from './types'; +import type { TaskExecutionPeriod } from './task'; import { LIST_DETECTION_RULE_EXCEPTION, LIST_ENDPOINT_EXCEPTION, @@ -30,6 +38,7 @@ import { DEFAULT_ADVANCED_POLICY_CONFIG_SETTINGS, } from './constants'; import { tagsToEffectScope } from '../../../common/endpoint/service/trusted_apps/mapping'; +import { resolverEntity } from '../../endpoint/routes/resolver/entity/utils/build_resolver_entity'; /** * Determines the when the last run was in order to execute to. @@ -345,3 +354,114 @@ export const processK8sUsernames = (clusterId: string, event: TelemetryEvent): T return event; }; + +export const ranges = ( + taskExecutionPeriod: TaskExecutionPeriod, + defaultIntervalInHours: number = 3 +) => { + const rangeFrom = taskExecutionPeriod.last ?? `now-${defaultIntervalInHours}h`; + const rangeTo = taskExecutionPeriod.current; + + return { rangeFrom, rangeTo }; +}; + +export class TelemetryTimelineFetcher { + startTime: number; + private receiver: ITelemetryReceiver; + private extraInfo: Promise; + private timeFrame: TimeFrame; + + constructor(receiver: ITelemetryReceiver) { + this.receiver = receiver; + this.startTime = Date.now(); + this.extraInfo = this.lookupExtraInfo(); + this.timeFrame = this.calculateTimeFrame(); + } + + async fetchTimeline(event: estypes.SearchHit): Promise { + const eventId = event._source ? event._source['event.id'] : 'unknown'; + const alertUUID = event._source ? event._source['kibana.alert.uuid'] : 'unknown'; + + const entities = resolverEntity([event]); + + // Build Tree + const tree = await this.receiver.buildProcessTree( + entities[0].id, + entities[0].schema, + this.timeFrame.startOfDay, + this.timeFrame.endOfDay + ); + + const nodeIds = Array.isArray(tree) ? tree.map((node) => node?.id.toString()) : []; + + const eventsStore = await this.fetchEventLineage(nodeIds); + + const telemetryTimeline: TimelineTelemetryEvent[] = Array.isArray(tree) + ? tree.map((node) => { + return { + ...node, + event: eventsStore.get(node.id.toString()), + }; + }) + : []; + + let record; + if (telemetryTimeline.length >= 1) { + const { clusterInfo, licenseInfo } = await this.extraInfo; + record = { + '@timestamp': moment().toISOString(), + version: clusterInfo.version?.number, + cluster_name: clusterInfo.cluster_name, + cluster_uuid: clusterInfo.cluster_uuid, + license_uuid: licenseInfo?.uid, + alert_id: alertUUID, + event_id: eventId, + timeline: telemetryTimeline, + }; + } + + const result: TimelineResult = { + nodes: nodeIds.length, + events: eventsStore.size, + timeline: record, + }; + + return result; + } + + private async fetchEventLineage(nodeIds: string[]): Promise> { + const timelineEvents = await this.receiver.fetchTimelineEvents(nodeIds); + const eventsStore = new Map(); + for (const event of timelineEvents.hits.hits) { + const doc = event._source; + + if (doc !== null && doc !== undefined) { + const entityId = doc?.process?.entity_id?.toString(); + if (entityId !== null && entityId !== undefined) eventsStore.set(entityId, doc); + } + } + return eventsStore; + } + + private async lookupExtraInfo(): Promise { + const [clusterInfoPromise, licenseInfoPromise] = await Promise.allSettled([ + this.receiver.fetchClusterInfo(), + this.receiver.fetchLicenseInfo(), + ]); + + const clusterInfo: ESClusterInfo = + clusterInfoPromise.status === 'fulfilled' ? clusterInfoPromise.value : ({} as ESClusterInfo); + + const licenseInfo: ESLicense | undefined = + licenseInfoPromise.status === 'fulfilled' ? licenseInfoPromise.value : ({} as ESLicense); + + return { clusterInfo, licenseInfo }; + } + + private calculateTimeFrame(): TimeFrame { + const now = moment(); + const startOfDay = now.startOf('day').toISOString(); + const endOfDay = now.endOf('day').toISOString(); + return { startOfDay, endOfDay }; + } +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index c699f6a1e9698..80b90deba6ef5 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -18,7 +18,7 @@ import type { SearchRequest, SearchResponse, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; import { EQL_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, @@ -43,6 +43,7 @@ import type { PackageService, } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; +import moment from 'moment'; import type { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { exceptionListItemToTelemetryEntry, @@ -70,6 +71,7 @@ import type { import { telemetryConfiguration } from './configuration'; import { ENDPOINT_METRICS_INDEX } from '../../../common/constants'; import { PREBUILT_RULES_PACKAGE_NAME } from '../../../common/detection_engine/constants'; +import { DEFAULT_DIAGNOSTIC_INDEX } from './constants'; export interface ITelemetryReceiver { start( @@ -83,6 +85,14 @@ export interface ITelemetryReceiver { getClusterInfo(): ESClusterInfo | undefined; + fetchClusterInfo(): Promise; + + fetchLicenseInfo(): Promise; + + openPointInTime(indexPattern: string): Promise; + + closePointInTime(pitId: string): Promise; + fetchDetectionRulesPackageVersion(): Promise; fetchFleetAgents(): Promise< @@ -149,9 +159,15 @@ export interface ITelemetryReceiver { per_page: number; }>; - fetchClusterInfo(): Promise; - - fetchLicenseInfo(): Promise; + fetchPrebuiltRuleAlertsBatch( + pitId: string, + searchAfterValue: SortResults | undefined + ): Promise<{ + moreToFetch: boolean; + newPitId: string; + searchAfter: SortResults | undefined; + alerts: TelemetryEvent[]; + }>; copyLicenseFields(lic: ESLicense): { issuer?: string | undefined; @@ -161,9 +177,11 @@ export interface ITelemetryReceiver { type: string; }; - fetchPrebuiltRuleAlerts(): Promise<{ events: TelemetryEvent[]; count: number }>; - - fetchTimelineEndpointAlerts(interval: number): Promise>>; + fetchTimelineAlerts( + index: string, + rangeFrom: string, + rangeTo: string + ): Promise>>; buildProcessTree( entityId: string, @@ -177,6 +195,8 @@ export interface ITelemetryReceiver { ): Promise>>; fetchValueListMetaData(interval: number): Promise; + + getAlertsIndex(): string | undefined; } export class TelemetryReceiver implements ITelemetryReceiver { @@ -224,6 +244,10 @@ export class TelemetryReceiver implements ITelemetryReceiver { return this.clusterInfo; } + public getAlertsIndex(): string | undefined { + return this.alertsIndex; + } + public async fetchDetectionRulesPackageVersion(): Promise { return this.packageService?.asInternalUser.getInstallation(PREBUILT_RULES_PACKAGE_NAME); } @@ -396,7 +420,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { const query = { expand_wildcards: ['open' as const, 'hidden' as const], - index: '.logs-endpoint.diagnostic.collection-*', + index: `${DEFAULT_DIAGNOSTIC_INDEX}-*`, ignore_unavailable: true, size: telemetryConfiguration.telemetry_max_buffer_size, body: { @@ -439,11 +463,12 @@ export class TelemetryReceiver implements ITelemetryReceiver { // Ensure list is created if it does not exist await this.exceptionListClient.createTrustedAppsList(); + const timeFrom = moment.utc().subtract(1, 'day').valueOf(); const results = await this.exceptionListClient.findExceptionListItem({ - listId: ENDPOINT_TRUSTED_APPS_LIST_ID, + listId: ENDPOINT_ARTIFACT_LISTS.trustedApps.id, page: 1, perPage: 10_000, - filter: undefined, + filter: `exception-list-agnostic.attributes.created_at >= ${timeFrom}`, namespaceType: 'agnostic', sortField: 'name', sortOrder: 'asc', @@ -465,11 +490,12 @@ export class TelemetryReceiver implements ITelemetryReceiver { // Ensure list is created if it does not exist await this.exceptionListClient.createEndpointList(); + const timeFrom = moment.utc().subtract(1, 'day').valueOf(); const results = await this.exceptionListClient.findExceptionListItem({ listId, page: 1, perPage: this.maxRecords, - filter: undefined, + filter: `exception-list-agnostic.attributes.created_at >= ${timeFrom}`, namespaceType: 'agnostic', sortField: 'name', sortOrder: 'asc', @@ -545,9 +571,14 @@ export class TelemetryReceiver implements ITelemetryReceiver { // Ensure list is created if it does not exist await this.exceptionListClient.createTrustedAppsList(); + const timeFrom = `exception-list.attributes.created_at >= ${moment + .utc() + .subtract(24, 'hours') + .valueOf()}`; + const results = await this.exceptionListClient?.findExceptionListsItem({ listId: [listId], - filter: [], + filter: [timeFrom], perPage: this.maxRecords, page: 1, sortField: 'exception-list.created_at', @@ -563,141 +594,191 @@ export class TelemetryReceiver implements ITelemetryReceiver { }; } - /** - * Fetch an overview of detection rule alerts over the last 3 hours. - * Filters out custom rules and endpoint rules. - * @returns total of alerts by rules - */ - public async fetchPrebuiltRuleAlerts() { + public async fetchPrebuiltRuleAlertsBatch( + pitId: string, + searchAfterValue: SortResults | undefined + ) { if (this.esClient === undefined || this.esClient === null) { - throw Error('elasticsearch client is unavailable: cannot retrieve pre-built rule alerts'); + throw Error('es client is unavailable: cannot retrieve pre-built rule alert batches'); } - const query: SearchRequest = { - expand_wildcards: ['open' as const, 'hidden' as const], - index: `${this.alertsIndex}*`, - ignore_unavailable: true, - body: { - size: 1_000, - _source: { - exclude: ['message', 'kibana.alert.rule.note', 'kibana.alert.rule.parameters.note'], - }, - query: { - bool: { - filter: [ - { - bool: { - should: [ - { - bool: { - must_not: { - bool: { - should: [ - { - match_phrase: { - 'kibana.alert.rule.name': 'Malware Prevention Alert', - }, + let newPitId = pitId; + let fetchMore = true; + let searchAfter: SortResults | undefined = searchAfterValue; + const query: ESSearchRequest = { + query: { + bool: { + filter: [ + { + bool: { + should: [ + { + bool: { + must_not: { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.rule.name': 'Malware Prevention Alert', }, - ], - }, + }, + ], }, }, }, - { - bool: { - must_not: { - bool: { - should: [ - { - match_phrase: { - 'kibana.alert.rule.name': 'Malware Detection Alert', - }, + }, + { + bool: { + must_not: { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.rule.name': 'Malware Detection Alert', }, - ], - }, + }, + ], }, }, }, - { - bool: { - must_not: { - bool: { - should: [ - { - match_phrase: { - 'kibana.alert.rule.name': 'Ransomware Prevention Alert', - }, + }, + { + bool: { + must_not: { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.rule.name': 'Ransomware Prevention Alert', }, - ], - }, + }, + ], }, }, }, - { - bool: { - must_not: { - bool: { - should: [ - { - match_phrase: { - 'kibana.alert.rule.name': 'Ransomware Detection Alert', - }, + }, + { + bool: { + must_not: { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.rule.name': 'Ransomware Detection Alert', }, - ], - }, + }, + ], }, }, }, - ], - }, + }, + ], }, - { - bool: { - should: [ - { - match_phrase: { - 'kibana.alert.rule.parameters.immutable': 'true', - }, + }, + { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.rule.parameters.immutable': 'true', }, - ], - }, - }, - { - range: { - '@timestamp': { - gte: 'now-1h', - lte: 'now', }, + ], + }, + }, + { + range: { + '@timestamp': { + gte: 'now-1h', + lte: 'now', }, }, - ], - }, - }, - aggs: { - prebuilt_rule_alert_count: { - cardinality: { - field: 'event.id', }, - }, + ], }, }, + track_total_hits: false, + sort: [ + { '@timestamp': { order: 'asc', format: 'strict_date_optional_time_nanos' } }, + { _shard_doc: 'desc' }, + ] as unknown as string[], + pit: { id: pitId }, + search_after: searchAfter, + size: 1_000, }; - const response = await this.esClient.search(query, { meta: true }); - tlog(this.logger, `received prebuilt alerts: (${response.body.hits.hits.length})`); + let response = null; + try { + response = await this.esClient.search(query); + const numOfHits = response?.hits.hits.length; + + if (numOfHits > 0) { + const lastHit = response?.hits.hits[numOfHits - 1]; + searchAfter = lastHit?.sort; + } + + fetchMore = numOfHits > 0 && numOfHits < 1_000; + } catch (e) { + tlog(this.logger, e); + fetchMore = false; + } + + if (response == null) { + return { + moreToFetch: false, + newPitId: pitId, + searchAfter, + alerts: [] as TelemetryEvent[], + }; + } - const telemetryEvents: TelemetryEvent[] = response.body.hits.hits.flatMap((h) => + const alerts: TelemetryEvent[] = response.hits.hits.flatMap((h) => h._source != null ? ([h._source] as TelemetryEvent[]) : [] ); - const aggregations = response.body?.aggregations as unknown as { - prebuilt_rule_alert_count: { value: number }; + if (response?.pit_id != null) { + newPitId = response?.pit_id; + } + + tlog(this.logger, `Prebuilt rule alerts to return: ${alerts.length}`); + + return { + moreToFetch: fetchMore, + newPitId, + searchAfter, + alerts, }; + } + + public async openPointInTime(indexPattern: string) { + if (this.esClient === undefined || this.esClient === null) { + throw Error('es client is unavailable: cannot retrieve pre-built rule alert batches'); + } + + const keepAlive = '5m'; + const pitId: OpenPointInTimeResponse['id'] = ( + await this.esClient.openPointInTime({ + index: `${indexPattern}*`, + keep_alive: keepAlive, + }) + ).id; + + return pitId; + } + + public async closePointInTime(pitId: string) { + if (this.esClient === undefined || this.esClient === null) { + throw Error('es client is unavailable: cannot retrieve pre-built rule alert batches'); + } - return { events: telemetryEvents, count: aggregations?.prebuilt_rule_alert_count.value ?? 0 }; + try { + await this.esClient.closePointInTime({ id: pitId }); + } catch (error) { + tlog(this.logger, `Error trying to close point in time: "${pitId}". Error is: "${error}"`); + } } - public async fetchTimelineEndpointAlerts(interval: number) { + async fetchTimelineAlerts(index: string, rangeFrom: string, rangeTo: string) { if (this.esClient === undefined || this.esClient === null) { throw Error('elasticsearch client is unavailable: cannot retrieve cluster infomation'); } @@ -708,7 +789,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { // create and assign an initial point in time let pitId: OpenPointInTimeResponse['id'] = ( await this.esClient.openPointInTime({ - index: `${this.alertsIndex}*`, + index: `${index}*`, keep_alive: keepAlive, }) ).id; @@ -746,8 +827,8 @@ export class TelemetryReceiver implements ITelemetryReceiver { { range: { '@timestamp': { - gte: `now-${interval}h`, - lte: 'now', + gte: rangeFrom, + lte: rangeTo, }, }, }, @@ -807,7 +888,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { } tlog(this.logger, `Timeline alerts to return: ${alertsToReturn.length}`); - return alertsToReturn; + return alertsToReturn || []; } public async buildProcessTree( diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index 39af813916cdd..041d9b67720d9 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -397,7 +397,7 @@ export class TelemetryEventsSender implements ITelemetryEventsSender { 'X-Elastic-Stack-Version': clusterVersionNumber ? clusterVersionNumber : '8.0.0', ...(licenseId ? { 'X-Elastic-License-ID': licenseId } : {}), }, - timeout: 5000, + timeout: 10000, }); this.telemetryUsageCounter?.incrementCounter({ counterName: createUsageCounterLabel(usageLabelPrefix.concat(['payloads', channel])), diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts index 4562cbb725cb4..00da024b0049e 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts @@ -11,7 +11,13 @@ import { TELEMETRY_CHANNEL_LISTS, TASK_METRICS_CHANNEL, } from '../constants'; -import { batchTelemetryRecords, templateExceptionList, tlog, createTaskMetric } from '../helpers'; +import { + batchTelemetryRecords, + templateExceptionList, + tlog, + createTaskMetric, + createUsageCounterLabel, +} from '../helpers'; import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryReceiver } from '../receiver'; import type { ExceptionListItem, ESClusterInfo, ESLicense, RuleSearchResult } from '../types'; @@ -31,8 +37,18 @@ export function createTelemetryDetectionRuleListsTaskConfig(maxTelemetryBatch: n sender: ITelemetryEventsSender, taskExecutionPeriod: TaskExecutionPeriod ) => { + const usageCollector = sender.getTelemetryUsageCluster(); + + const usageLabelPrefix: string[] = ['security_telemetry', 'detection-rules']; + const startTime = Date.now(); const taskName = 'Security Solution Detection Rule Lists Telemetry'; + + tlog( + logger, + `Running task: ${taskId} [last: ${taskExecutionPeriod.last} - current: ${taskExecutionPeriod.current}]` + ); + try { const [clusterInfoPromise, licenseInfoPromise] = await Promise.allSettled([ receiver.fetchClusterInfo(), @@ -98,6 +114,13 @@ export function createTelemetryDetectionRuleListsTaskConfig(maxTelemetryBatch: n LIST_DETECTION_RULE_EXCEPTION ); tlog(logger, `Detection rule exception json length ${detectionRuleExceptionsJson.length}`); + + usageCollector?.incrementCounter({ + counterName: createUsageCounterLabel(usageLabelPrefix), + counterType: 'detection_rule_count', + incrementBy: detectionRuleExceptionsJson.length, + }); + const batches = batchTelemetryRecords(detectionRuleExceptionsJson, maxTelemetryBatch); for (const batch of batches) { await sender.sendOnDemand(TELEMETRY_CHANNEL_LISTS, batch); @@ -105,7 +128,7 @@ export function createTelemetryDetectionRuleListsTaskConfig(maxTelemetryBatch: n await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ createTaskMetric(taskName, true, startTime), ]); - return detectionRuleExceptions.length; + return detectionRuleExceptionsJson.length; } catch (err) { await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ createTaskMetric(taskName, false, startTime, err.message), diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts index e25b3690ee88d..d237757616f3e 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts @@ -12,6 +12,7 @@ import { createTelemetrySecurityListTaskConfig } from './security_lists'; import { createTelemetryDetectionRuleListsTaskConfig } from './detection_rule'; import { createTelemetryPrebuiltRuleAlertsTaskConfig } from './prebuilt_rule_alerts'; import { createTelemetryTimelineTaskConfig } from './timelines'; +import { createTelemetryDiagnosticTimelineTaskConfig } from './timelines_diagnostic'; import { createTelemetryConfigurationTaskConfig } from './configuration'; import { telemetryConfiguration } from '../configuration'; import { createTelemetryFilterListArtifactTaskConfig } from './filterlists'; @@ -26,6 +27,7 @@ export function createTelemetryTaskConfigs(): SecurityTelemetryTaskConfig[] { ), createTelemetryPrebuiltRuleAlertsTaskConfig(telemetryConfiguration.max_detection_alerts_batch), createTelemetryTimelineTaskConfig(), + createTelemetryDiagnosticTimelineTaskConfig(), createTelemetryConfigurationTaskConfig(), createTelemetryFilterListArtifactTaskConfig(), ]; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.test.ts index de5219a7f1fa2..479ceabd65a3b 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.test.ts @@ -42,11 +42,7 @@ describe('security telemetry - detection rule alerts task test', () => { testTaskExecutionPeriod ); expect(mockTelemetryReceiver.fetchDetectionRulesPackageVersion).toHaveBeenCalled(); - expect(mockTelemetryReceiver.fetchPrebuiltRuleAlerts).toHaveBeenCalled(); - expect(mockTelemetryEventsSender.getTelemetryUsageCluster).toHaveBeenCalled(); - expect(mockTelemetryEventsSender.getTelemetryUsageCluster()?.incrementCounter).toBeCalledTimes( - 1 - ); + expect(mockTelemetryReceiver.fetchPrebuiltRuleAlertsBatch).toHaveBeenCalled(); expect(mockTelemetryEventsSender.sendOnDemand).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts index 0fdc6cf32a69c..0765d1d5bbdae 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/prebuilt_rule_alerts.ts @@ -6,6 +6,7 @@ */ import type { Logger } from '@kbn/core/server'; +import type { SortResults } from '@elastic/elasticsearch/lib/api/types'; import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryReceiver } from '../receiver'; import type { ESClusterInfo, ESLicense, TelemetryEvent } from '../types'; @@ -15,12 +16,14 @@ import { batchTelemetryRecords, createTaskMetric, processK8sUsernames, tlog } fr import { copyAllowlistedFields, filterList } from '../filterlists'; export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: number) { + const taskVersion = '1.2.0'; + return { type: 'security:telemetry-prebuilt-rule-alerts', title: 'Security Solution - Prebuilt Rule and Elastic ML Alerts Telemetry', interval: '1h', - timeout: '5m', - version: '1.0.0', + timeout: '15m', + version: taskVersion, runTask: async ( taskId: string, logger: Logger, @@ -47,53 +50,62 @@ export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: n : ({} as ESLicense | undefined); const packageInfo = packageVersion.status === 'fulfilled' ? packageVersion.value : undefined; + const index = receiver.getAlertsIndex(); - const { events: telemetryEvents, count: totalPrebuiltAlertCount } = - await receiver.fetchPrebuiltRuleAlerts(); - - sender.getTelemetryUsageCluster()?.incrementCounter({ - counterName: 'telemetry_prebuilt_rule_alerts', - counterType: 'prebuilt_alert_count', - incrementBy: totalPrebuiltAlertCount, - }); - - if (telemetryEvents.length === 0) { - tlog(logger, 'no prebuilt rule alerts retrieved'); - await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ - createTaskMetric(taskName, true, startTime), - ]); + if (index === undefined) { + tlog(logger, `alerts index is not ready yet, skipping telemetry task`); return 0; } - const processedAlerts = telemetryEvents.map( - (event: TelemetryEvent): TelemetryEvent => - copyAllowlistedFields(filterList.prebuiltRulesAlerts, event) - ); - - const sanitizedAlerts = processedAlerts.map( - (event: TelemetryEvent): TelemetryEvent => - processK8sUsernames(clusterInfo?.cluster_uuid, event) - ); - - const enrichedAlerts = sanitizedAlerts.map( - (event: TelemetryEvent): TelemetryEvent => ({ - ...event, - licence_id: licenseInfo?.uid, - cluster_uuid: clusterInfo?.cluster_uuid, - cluster_name: clusterInfo?.cluster_name, - package_version: packageInfo?.version, - }) - ); - - tlog(logger, `sending ${enrichedAlerts.length} elastic prebuilt alerts`); - const batches = batchTelemetryRecords(enrichedAlerts, maxTelemetryBatch); - for (const batch of batches) { - await sender.sendOnDemand(TELEMETRY_CHANNEL_DETECTION_ALERTS, batch); + let fetchMore = true; + let searchAfterValue: SortResults | undefined; + let pitId = await receiver.openPointInTime(index); + + while (fetchMore) { + const { moreToFetch, newPitId, searchAfter, alerts } = + await receiver.fetchPrebuiltRuleAlertsBatch(pitId, searchAfterValue); + + if (alerts.length === 0) { + return 0; + } + + fetchMore = moreToFetch; + searchAfterValue = searchAfter; + pitId = newPitId; + + const processedAlerts = alerts.map( + (event: TelemetryEvent): TelemetryEvent => + copyAllowlistedFields(filterList.prebuiltRulesAlerts, event) + ); + + const sanitizedAlerts = processedAlerts.map( + (event: TelemetryEvent): TelemetryEvent => + processK8sUsernames(clusterInfo?.cluster_uuid, event) + ); + + const enrichedAlerts = sanitizedAlerts.map( + (event: TelemetryEvent): TelemetryEvent => ({ + ...event, + licence_id: licenseInfo?.uid, + cluster_uuid: clusterInfo?.cluster_uuid, + cluster_name: clusterInfo?.cluster_name, + package_version: packageInfo?.version, + task_version: taskVersion, + }) + ); + + tlog(logger, `sending ${enrichedAlerts.length} elastic prebuilt alerts`); + const batches = batchTelemetryRecords(enrichedAlerts, maxTelemetryBatch); + + const promises = batches.map(async (batch) => { + sender.sendOnDemand(TELEMETRY_CHANNEL_DETECTION_ALERTS, batch); + }); + + await Promise.all(promises); } - await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ - createTaskMetric(taskName, true, startTime), - ]); - return enrichedAlerts.length; + + await receiver.closePointInTime(pitId); + return 0; } catch (err) { logger.error('could not complete prebuilt alerts telemetry task'); await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts index 04312e8843e28..863d66d55c4e7 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts @@ -6,10 +6,7 @@ */ import type { Logger } from '@kbn/core/server'; -import { - ENDPOINT_LIST_ID, - ENDPOINT_EVENT_FILTERS_LIST_ID, -} from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_LIST_ID, ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER, @@ -23,6 +20,8 @@ import { templateExceptionList, createTaskMetric, formatValueListMetaData, + createUsageCounterLabel, + tlog, } from '../helpers'; import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryReceiver } from '../receiver'; @@ -42,10 +41,16 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) sender: ITelemetryEventsSender, taskExecutionPeriod: TaskExecutionPeriod ) => { + const usageCollector = sender.getTelemetryUsageCluster(); + + const usageLabelPrefix: string[] = ['security_telemetry', 'lists']; + const startTime = Date.now(); const taskName = 'Security Solution Lists Telemetry'; try { - let count = 0; + let trustedApplicationsCount = 0; + let endpointExceptionsCount = 0; + let endpointEventFiltersCount = 0; const [clusterInfoPromise, licenseInfoPromise] = await Promise.allSettled([ receiver.fetchClusterInfo(), @@ -71,7 +76,14 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) licenseInfo, LIST_TRUSTED_APPLICATION ); - count += trustedAppsJson.length; + trustedApplicationsCount = trustedAppsJson.length; + tlog(logger, `Trusted Apps: ${trustedApplicationsCount}`); + + usageCollector?.incrementCounter({ + counterName: createUsageCounterLabel(usageLabelPrefix), + counterType: 'trusted_apps_count', + incrementBy: trustedApplicationsCount, + }); const batches = batchTelemetryRecords(trustedAppsJson, maxTelemetryBatch); for (const batch of batches) { @@ -89,7 +101,14 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) licenseInfo, LIST_ENDPOINT_EXCEPTION ); - count += epExceptionsJson.length; + endpointExceptionsCount = epExceptionsJson.length; + tlog(logger, `EP Exceptions: ${endpointExceptionsCount}`); + + usageCollector?.incrementCounter({ + counterName: createUsageCounterLabel(usageLabelPrefix), + counterType: 'endpoint_exceptions_count', + incrementBy: endpointExceptionsCount, + }); const batches = batchTelemetryRecords(epExceptionsJson, maxTelemetryBatch); for (const batch of batches) { @@ -99,7 +118,7 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) // Lists Telemetry: Endpoint Event Filters - const epFilters = await receiver.fetchEndpointList(ENDPOINT_EVENT_FILTERS_LIST_ID); + const epFilters = await receiver.fetchEndpointList(ENDPOINT_ARTIFACT_LISTS.eventFilters.id); if (epFilters?.data) { const epFiltersJson = templateExceptionList( epFilters.data, @@ -107,7 +126,14 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) licenseInfo, LIST_ENDPOINT_EVENT_FILTER ); - count += epFiltersJson.length; + endpointEventFiltersCount = epFiltersJson.length; + tlog(logger, `EP Event Filters: ${endpointEventFiltersCount}`); + + usageCollector?.incrementCounter({ + counterName: createUsageCounterLabel(usageLabelPrefix), + counterType: 'endpoint_event_filters_count', + incrementBy: endpointEventFiltersCount, + }); const batches = batchTelemetryRecords(epFiltersJson, maxTelemetryBatch); for (const batch of batches) { @@ -130,7 +156,7 @@ export function createTelemetrySecurityListTaskConfig(maxTelemetryBatch: number) await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ createTaskMetric(taskName, true, startTime), ]); - return count; + return trustedApplicationsCount + endpointExceptionsCount + endpointEventFiltersCount; } catch (err) { await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ createTaskMetric(taskName, false, startTime, err.message), diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/tasks.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/tasks.test.ts index f91de7fe5b194..75da7e5084ab2 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/tasks.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/tasks.test.ts @@ -52,8 +52,8 @@ describe('security telemetry - ', () => { expect(taskConfig.interval).toEqual('24h'); }); - test('timelines task is set to 3h', async () => { + test('timelines task is set to 1h', async () => { const taskConfig = createTelemetryTimelineTaskConfig(); - expect(taskConfig.interval).toEqual('3h'); + expect(taskConfig.interval).toEqual('1h'); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.test.ts index 16794dfa3f68f..930ef076edd3f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.test.ts @@ -35,7 +35,7 @@ describe('timeline telemetry task test', () => { expect(mockTelemetryReceiver.buildProcessTree).toHaveBeenCalled(); expect(mockTelemetryReceiver.fetchTimelineEvents).toHaveBeenCalled(); - expect(mockTelemetryReceiver.fetchTimelineEndpointAlerts).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineAlerts).toHaveBeenCalled(); expect(mockTelemetryEventsSender.getTelemetryUsageCluster).toHaveBeenCalled(); expect(mockTelemetryEventsSender.sendOnDemand).toHaveBeenCalled(); }); @@ -59,6 +59,6 @@ describe('timeline telemetry task test', () => { expect(mockTelemetryReceiver.buildProcessTree).toHaveBeenCalled(); expect(mockTelemetryReceiver.fetchTimelineEvents).toHaveBeenCalled(); - expect(mockTelemetryReceiver.fetchTimelineEndpointAlerts).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineAlerts).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.ts index bd50ffcd92817..7e0c41e57eec4 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines.ts @@ -5,29 +5,22 @@ * 2.0. */ -import moment from 'moment'; import type { Logger } from '@kbn/core/server'; -import type { SafeEndpointEvent } from '../../../../common/endpoint/types'; import type { ITelemetryEventsSender } from '../sender'; import type { ITelemetryReceiver } from '../receiver'; import type { TaskExecutionPeriod } from '../task'; -import type { - ESClusterInfo, - ESLicense, - TimelineTelemetryTemplate, - TimelineTelemetryEvent, -} from '../types'; import { TELEMETRY_CHANNEL_TIMELINE, TASK_METRICS_CHANNEL } from '../constants'; -import { resolverEntity } from '../../../endpoint/routes/resolver/entity/utils/build_resolver_entity'; -import { tlog, createTaskMetric } from '../helpers'; +import { createTaskMetric, ranges, TelemetryTimelineFetcher, tlog } from '../helpers'; export function createTelemetryTimelineTaskConfig() { + const taskName = 'Security Solution Timeline telemetry'; + return { type: 'security:telemetry-timelines', - title: 'Security Solution Timeline telemetry', - interval: '3h', - timeout: '10m', - version: '1.0.0', + title: taskName, + interval: '1h', + timeout: '15m', + version: '1.0.1', runTask: async ( taskId: string, logger: Logger, @@ -35,142 +28,59 @@ export function createTelemetryTimelineTaskConfig() { sender: ITelemetryEventsSender, taskExecutionPeriod: TaskExecutionPeriod ) => { - const startTime = Date.now(); - const taskName = 'Security Solution Timeline telemetry'; - try { - let counter = 0; - - tlog(logger, `Running task: ${taskId}`); - - const [clusterInfoPromise, licenseInfoPromise] = await Promise.allSettled([ - receiver.fetchClusterInfo(), - receiver.fetchLicenseInfo(), - ]); - - const clusterInfo = - clusterInfoPromise.status === 'fulfilled' - ? clusterInfoPromise.value - : ({} as ESClusterInfo); - - const licenseInfo = - licenseInfoPromise.status === 'fulfilled' - ? licenseInfoPromise.value - : ({} as ESLicense | undefined); + tlog( + logger, + `Running task: ${taskId} [last: ${taskExecutionPeriod.last} - current: ${taskExecutionPeriod.current}]` + ); - const now = moment(); - const startOfDay = now.startOf('day').toISOString(); - const endOfDay = now.endOf('day').toISOString(); + const fetcher = new TelemetryTimelineFetcher(receiver); - const baseDocument = { - version: clusterInfo.version?.number, - cluster_name: clusterInfo.cluster_name, - cluster_uuid: clusterInfo.cluster_uuid, - license_uuid: licenseInfo?.uid, - }; - - // Fetch EP Alerts + try { + let counter = 0; - const endpointAlerts = await receiver.fetchTimelineEndpointAlerts(3); + const { rangeFrom, rangeTo } = ranges(taskExecutionPeriod); - // No EP Alerts -> Nothing to do - if (endpointAlerts.length === 0 || endpointAlerts.length === undefined) { - tlog(logger, 'no endpoint alerts received. exiting telemetry task.'); - await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ - createTaskMetric(taskName, true, startTime), - ]); - return counter; + const alertsIndex = receiver.getAlertsIndex(); + if (!alertsIndex) { + throw Error('alerts index is not ready yet, skipping telemetry task'); } + const alerts = await receiver.fetchTimelineAlerts(alertsIndex, rangeFrom, rangeTo); - // Build process tree for each EP Alert recieved - - for (const alert of endpointAlerts) { - const eventId = alert._source ? alert._source['event.id'] : 'unknown'; - const alertUUID = alert._source ? alert._source['kibana.alert.uuid'] : 'unknown'; + tlog(logger, `found ${alerts.length} alerts to process`); - const entities = resolverEntity([alert]); - - // Build Tree - - const tree = await receiver.buildProcessTree( - entities[0].id, - entities[0].schema, - startOfDay, - endOfDay - ); - - const nodeIds = [] as string[]; - if (Array.isArray(tree)) { - for (const node of tree) { - const nodeId = node?.id.toString(); - nodeIds.push(nodeId); - } - } + for (const alert of alerts) { + const result = await fetcher.fetchTimeline(alert); sender.getTelemetryUsageCluster()?.incrementCounter({ counterName: 'telemetry_timeline', counterType: 'timeline_node_count', - incrementBy: nodeIds.length, + incrementBy: result.nodes, }); - // Fetch event lineage - - const timelineEvents = await receiver.fetchTimelineEvents(nodeIds); - const eventsStore = new Map(); - for (const event of timelineEvents.hits.hits) { - const doc = event._source; - - if (doc !== null && doc !== undefined) { - const entityId = doc?.process?.entity_id?.toString(); - if (entityId !== null && entityId !== undefined) eventsStore.set(entityId, doc); - } - } - sender.getTelemetryUsageCluster()?.incrementCounter({ counterName: 'telemetry_timeline', counterType: 'timeline_event_count', - incrementBy: eventsStore.size, + incrementBy: result.events, }); - // Create telemetry record - - const telemetryTimeline: TimelineTelemetryEvent[] = []; - if (Array.isArray(tree)) { - for (const node of tree) { - const id = node.id.toString(); - const event = eventsStore.get(id); - - const timelineTelemetryEvent: TimelineTelemetryEvent = { - ...node, - event, - }; - - telemetryTimeline.push(timelineTelemetryEvent); - } - } - - if (telemetryTimeline.length >= 1) { - const record: TimelineTelemetryTemplate = { - '@timestamp': moment().toISOString(), - ...baseDocument, - alert_id: alertUUID, - event_id: eventId, - timeline: telemetryTimeline, - }; - - sender.sendOnDemand(TELEMETRY_CHANNEL_TIMELINE, [record]); + if (result.timeline) { + sender.sendOnDemand(TELEMETRY_CHANNEL_TIMELINE, [result.timeline]); counter += 1; } else { tlog(logger, 'no events in timeline'); } } - tlog(logger, `sent ${counter} timelines. concluding timeline task.`); + + tlog(logger, `sent ${counter} timelines. Concluding timeline task.`); + await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ - createTaskMetric(taskName, true, startTime), + createTaskMetric(taskName, true, fetcher.startTime), ]); + return counter; } catch (err) { await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ - createTaskMetric(taskName, false, startTime, err.message), + createTaskMetric(taskName, false, fetcher.startTime, err.message), ]); return 0; } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.test.ts new file mode 100644 index 0000000000000..887e0789e7754 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { createTelemetryDiagnosticTimelineTaskConfig } from './timelines_diagnostic'; +import { createMockTelemetryEventsSender, createMockTelemetryReceiver } from '../__mocks__'; + +describe('timeline telemetry diagnostic task test', () => { + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + }); + + test('timeline telemetry task should be correctly set up', async () => { + const testTaskExecutionPeriod = { + last: undefined, + current: new Date().toISOString(), + }; + const mockTelemetryEventsSender = createMockTelemetryEventsSender(); + const mockTelemetryReceiver = createMockTelemetryReceiver(); + const telemetryTelemetryDiagnosticTaskConfig = createTelemetryDiagnosticTimelineTaskConfig(); + + await telemetryTelemetryDiagnosticTaskConfig.runTask( + 'test-timeline-diagnostic-task-id', + logger, + mockTelemetryReceiver, + mockTelemetryEventsSender, + testTaskExecutionPeriod + ); + + expect(mockTelemetryReceiver.buildProcessTree).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineEvents).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineAlerts).toHaveBeenCalled(); + expect(mockTelemetryEventsSender.getTelemetryUsageCluster).toHaveBeenCalled(); + expect(mockTelemetryEventsSender.sendOnDemand).toHaveBeenCalled(); + }); + + test('if no timeline events received it should not send a telemetry record', async () => { + const testTaskExecutionPeriod = { + last: undefined, + current: new Date().toISOString(), + }; + const mockTelemetryEventsSender = createMockTelemetryEventsSender(); + const mockTelemetryReceiver = createMockTelemetryReceiver(null, true); + const telemetryTelemetryDiagnosticTaskConfig = createTelemetryDiagnosticTimelineTaskConfig(); + + await telemetryTelemetryDiagnosticTaskConfig.runTask( + 'test-timeline-diagnostic-task-id', + logger, + mockTelemetryReceiver, + mockTelemetryEventsSender, + testTaskExecutionPeriod + ); + + expect(mockTelemetryReceiver.buildProcessTree).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineEvents).toHaveBeenCalled(); + expect(mockTelemetryReceiver.fetchTimelineAlerts).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.ts new file mode 100644 index 0000000000000..7148848984b0a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/timelines_diagnostic.ts @@ -0,0 +1,93 @@ +/* + * Copyright 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 { Logger } from '@kbn/core/server'; +import type { ITelemetryEventsSender } from '../sender'; +import type { ITelemetryReceiver } from '../receiver'; +import type { TaskExecutionPeriod } from '../task'; +import { + DEFAULT_DIAGNOSTIC_INDEX, + TELEMETRY_CHANNEL_TIMELINE, + TASK_METRICS_CHANNEL, +} from '../constants'; +import { createTaskMetric, ranges, TelemetryTimelineFetcher, tlog } from '../helpers'; + +export function createTelemetryDiagnosticTimelineTaskConfig() { + const taskName = 'Security Solution Diagnostic Timeline telemetry'; + + return { + type: 'security:telemetry-diagnostic-timelines', + title: taskName, + interval: '1h', + timeout: '15m', + version: '1.0.0', + runTask: async ( + taskId: string, + logger: Logger, + receiver: ITelemetryReceiver, + sender: ITelemetryEventsSender, + taskExecutionPeriod: TaskExecutionPeriod + ) => { + tlog( + logger, + `Running task: ${taskId} [last: ${taskExecutionPeriod.last} - current: ${taskExecutionPeriod.current}]` + ); + + const fetcher = new TelemetryTimelineFetcher(receiver); + + try { + let counter = 0; + + const { rangeFrom, rangeTo } = ranges(taskExecutionPeriod); + + const alerts = await receiver.fetchTimelineAlerts( + DEFAULT_DIAGNOSTIC_INDEX, + rangeFrom, + rangeTo + ); + + tlog(logger, `found ${alerts.length} alerts to process`); + + for (const alert of alerts) { + const result = await fetcher.fetchTimeline(alert); + + sender.getTelemetryUsageCluster()?.incrementCounter({ + counterName: 'telemetry_timeline_diagnostic', + counterType: 'timeline_diagnostic_node_count', + incrementBy: result.nodes, + }); + + sender.getTelemetryUsageCluster()?.incrementCounter({ + counterName: 'telemetry_timeline_diagnostic', + counterType: 'timeline_diagnostic_event_count', + incrementBy: result.events, + }); + + if (result.timeline) { + sender.sendOnDemand(TELEMETRY_CHANNEL_TIMELINE, [result.timeline]); + counter += 1; + } else { + tlog(logger, 'no events in timeline'); + } + } + + tlog(logger, `sent ${counter} timelines. Concluding timeline task.`); + + await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ + createTaskMetric(taskName, true, fetcher.startTime), + ]); + + return counter; + } catch (err) { + await sender.sendOnDemand(TASK_METRICS_CHANNEL, [ + createTaskMetric(taskName, false, fetcher.startTime, err.message), + ]); + return 0; + } + }, + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts index df3b571714b29..433c1c01cf0bc 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -457,3 +457,19 @@ export interface ValueListResponse { exceptionListMetricsResponse: ValueListExceptionListResponseAggregation; indicatorMatchMetricsResponse: ValueListIndicatorMatchResponseAggregation; } + +export interface ExtraInfo { + clusterInfo: ESClusterInfo; + licenseInfo: ESLicense | undefined; +} + +export interface TimeFrame { + startOfDay: string; + endOfDay: string; +} + +export interface TimelineResult { + nodes: number; + events: number; + timeline: TimelineTelemetryTemplate | undefined; +} diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts new file mode 100644 index 0000000000000..33a0b6f09d3e6 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SetupPlugins } from '../../../plugin'; +import type { SecuritySolutionPluginRouter } from '../../../types'; +import type { ConfigType } from '../../..'; +import { + createTimelinesRoute, + deleteTimelinesRoute, + exportTimelinesRoute, + getTimelineRoute, + getTimelinesRoute, + importTimelinesRoute, + patchTimelinesRoute, + persistFavoriteRoute, + resolveTimelineRoute, + copyTimelineRoute, +} from './timelines'; +import { getDraftTimelinesRoute } from './draft_timelines/get_draft_timelines'; +import { cleanDraftTimelinesRoute } from './draft_timelines/clean_draft_timelines'; +import { installPrepackedTimelinesRoute } from './prepackaged_timelines/install_prepackaged_timelines'; + +import { persistNoteRoute, deleteNoteRoute } from './notes'; + +import { persistPinnedEventRoute } from './pinned_events'; + +export function registerTimelineRoutes( + router: SecuritySolutionPluginRouter, + config: ConfigType, + security: SetupPlugins['security'] +) { + createTimelinesRoute(router, config, security); + patchTimelinesRoute(router, config, security); + + importTimelinesRoute(router, config, security); + exportTimelinesRoute(router, config, security); + getDraftTimelinesRoute(router, config, security); + getTimelineRoute(router, config, security); + resolveTimelineRoute(router, config, security); + getTimelinesRoute(router, config, security); + cleanDraftTimelinesRoute(router, config, security); + deleteTimelinesRoute(router, config, security); + persistFavoriteRoute(router, config, security); + copyTimelineRoute(router, config, security); + + installPrepackedTimelinesRoute(router, config, security); + + persistNoteRoute(router, config, security); + deleteNoteRoute(router, config, security); + persistPinnedEventRoute(router, config, security); +} diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts new file mode 100644 index 0000000000000..3a55b4fef8086 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; +import type { ConfigType } from '../../../../..'; +import { copyTimelineSchema } from '../../../../../../common/api/timeline'; +import { copyTimeline } from '../../../saved_object/timelines'; +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import type { SetupPlugins } from '../../../../../plugin'; +import { TIMELINE_COPY_URL } from '../../../../../../common/constants'; +import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; + +import { buildFrameworkRequest } from '../../../utils/common'; + +export const copyTimelineRoute = async ( + router: SecuritySolutionPluginRouter, + _: ConfigType, + security: SetupPlugins['security'] +) => { + router.versioned + .post({ + path: TIMELINE_COPY_URL, + options: { + tags: ['access:securitySolution'], + }, + access: 'internal', + }) + .addVersion( + { + version: '1', + validate: { + request: { body: buildRouteValidationWithExcess(copyTimelineSchema) }, + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { timeline, timelineIdToCopy } = request.body; + const copiedTimeline = await copyTimeline(frameworkRequest, timeline, timelineIdToCopy); + + return response.ok({ + body: { data: { persistTimeline: copiedTimeline } }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/index.ts index ba20633a65145..1f26af72a22e9 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/index.ts @@ -13,3 +13,4 @@ export { importTimelinesRoute } from './import_timelines'; export { patchTimelinesRoute } from './patch_timelines'; export { persistFavoriteRoute } from './persist_favorite'; export { resolveTimelineRoute } from './resolve_timeline'; +export { copyTimelineRoute } from './copy_timeline'; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.test.ts index 527c46598e922..1fbc51871447f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import type { Note } from '../../../../../common/api/timeline'; import { pickSavedNote } from './saved_object'; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts index 7169938e335dd..8527b0970f4fd 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts @@ -14,7 +14,7 @@ import { map, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import type { SavedObjectsFindOptions } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { getUserDisplayName } from '@kbn/user-profile-components'; import { UNAUTHENTICATED_USER } from '../../../../../common/constants'; import type { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts index 6dad7e61f0b2e..96f2aa3d1c26b 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts @@ -12,7 +12,7 @@ import { map, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import type { SavedObjectsFindOptions } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { UNAUTHENTICATED_USER } from '../../../../../common/constants'; import type { BarePinnedEvent, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.test.ts index 67ff652176161..9cefecda1ed2a 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.test.ts @@ -7,6 +7,7 @@ import type { FrameworkRequest } from '../../../framework'; import { mockGetTimelineValue, mockSavedObject } from '../../__mocks__/import_timelines'; +import { mockTimeline } from '../../__mocks__/create_timelines'; import { convertStringToBase64, @@ -15,10 +16,11 @@ import { getDraftTimeline, resolveTimelineOrNull, updatePartialSavedTimeline, + copyTimeline, } from '.'; import { convertSavedObjectToSavedTimeline } from './convert_saved_object_to_savedtimeline'; -import { getNotesByTimelineId } from '../notes/saved_object'; -import { getAllPinnedEventsByTimelineId } from '../pinned_events'; +import { getNotesByTimelineId, persistNote } from '../notes/saved_object'; +import { getAllPinnedEventsByTimelineId, persistPinnedEventOnTimeline } from '../pinned_events'; import { TimelineType } from '../../../../../common/api/timeline'; import type { AllTimelinesResponse, @@ -40,10 +42,12 @@ jest.mock('./convert_saved_object_to_savedtimeline', () => ({ jest.mock('../notes/saved_object', () => ({ getNotesByTimelineId: jest.fn().mockResolvedValue([]), + persistNote: jest.fn(), })); jest.mock('../pinned_events', () => ({ getAllPinnedEventsByTimelineId: jest.fn().mockResolvedValue([]), + persistPinnedEventOnTimeline: jest.fn(), })); describe('saved_object', () => { @@ -462,4 +466,95 @@ describe('saved_object', () => { }); }); }); + + describe('Copy timeline', () => { + let mockFindSavedObject: jest.Mock; + let mockRequest: FrameworkRequest; + let createSavedObject: jest.Mock; + + beforeEach(() => { + mockFindSavedObject = jest.fn().mockResolvedValue({ saved_objects: [], total: 0 }); + createSavedObject = jest.fn().mockResolvedValue({ + id: '1', + version: '2323r23', + attributes: { + ...mockGetTimelineValue, + kqlQuery: null, + }, + }); + mockRequest = { + user: { + username: 'username', + }, + context: { + core: { + savedObjects: { + client: { + find: mockFindSavedObject, + create: createSavedObject, + get: jest.fn(async () => ({ + ...mockResolvedSavedObject.saved_object, + })), + }, + }, + }, + }, + } as unknown as FrameworkRequest; + }); + + afterEach(() => { + mockFindSavedObject.mockClear(); + (getNotesByTimelineId as jest.Mock).mockClear(); + (persistNote as jest.Mock).mockClear(); + (getAllPinnedEventsByTimelineId as jest.Mock).mockClear(); + }); + + it('should resolve all associated saved objects and copy those', async () => { + const note = { + notedId: 'theNoteId', + timelineId: 'original_id', + version: '23d23f', + note: 'test note', + }; + (getNotesByTimelineId as jest.Mock).mockResolvedValue([note]); + const pinnedEvent = { + timelineId: 'original_id', + eventId: 'randomEventId', + }; + (getAllPinnedEventsByTimelineId as jest.Mock).mockResolvedValue([pinnedEvent]); + + const originalId = 'original_id'; + const res = await copyTimeline( + mockRequest, + mockTimeline as unknown as SavedTimeline, + originalId + ); + + // Resolves objects by the correct timeline id + expect(getNotesByTimelineId).toHaveBeenCalledWith(mockRequest, originalId); + expect(getAllPinnedEventsByTimelineId).toHaveBeenCalledWith(mockRequest, originalId); + + // Notes are created with the new timeline id and a copy of the original node + expect(persistNote).toHaveBeenCalledWith( + expect.objectContaining({ + noteId: null, + note: expect.objectContaining({ + ...note, + timelineId: mockResolvedTimeline.savedObjectId, + }), + overrideOwner: false, + }) + ); + + // Pinned events are created with the new timeline id and the correct event id + expect(persistPinnedEventOnTimeline).toHaveBeenCalledWith( + mockRequest, + null, + pinnedEvent.eventId, + mockResolvedTimeline.savedObjectId + ); + + expect(res.timeline.savedObjectId).toBe(mockResolvedTimeline.savedObjectId); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts index e966a2d9f9f32..9cdc9189b16fa 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts @@ -586,6 +586,63 @@ export const deleteTimeline = async (request: FrameworkRequest, timelineIds: str ); }; +export const copyTimeline = async ( + request: FrameworkRequest, + timeline: SavedTimeline, + timelineId: string +): Promise => { + const savedObjectsClient = (await request.context.core).savedObjects.client; + + // Fetch all objects that need to be copied + const [notes, pinnedEvents] = await Promise.all([ + note.getNotesByTimelineId(request, timelineId), + pinnedEvent.getAllPinnedEventsByTimelineId(request, timelineId), + ]); + + const isImmutable = timeline.status === TimelineStatus.immutable; + const userInfo = isImmutable ? ({ username: 'Elastic' } as AuthenticatedUser) : request.user; + + const timelineResponse = await createTimeline({ + savedObjectsClient, + timeline, + timelineId: null, + userInfo, + }); + + const newTimelineId = timelineResponse.timeline.savedObjectId; + + const copiedNotes = Promise.all( + notes.map((_note) => { + return note.persistNote({ + request, + noteId: null, + note: { + ..._note, + timelineId: newTimelineId, + }, + overrideOwner: false, + }); + }) + ); + + const copiedPinnedEvents = pinnedEvents.map((_pinnedEvent) => { + return pinnedEvent.persistPinnedEventOnTimeline( + request, + null, + _pinnedEvent.eventId, + newTimelineId + ); + }); + + await Promise.all([copiedNotes, copiedPinnedEvents]); + + return { + code: 200, + message: 'success', + timeline: await getSavedTimeline(request, newTimelineId), + }; +}; + const resolveBasicSavedTimeline = async (request: FrameworkRequest, timelineId: string) => { const savedObjectsClient = (await request.context.core).savedObjects.client; const { saved_object: savedObject, ...resolveAttributes } = diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.test.ts index 98bc33fee3759..db32de9dc65ce 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import type { SavedTimeline, Note } from '../../../../../common/api/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.ts index bc0c9075887bd..90467a4568244 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/pick_saved_timeline.ts @@ -6,7 +6,7 @@ */ import { isEmpty } from 'lodash/fp'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common/model'; +import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { getUserDisplayName } from '@kbn/user-profile-components'; import { UNAUTHENTICATED_USER } from '../../../../../common/constants'; import type { SavedTimelineWithSavedObjectId } from '../../../../../common/api/timeline'; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index c3e20042d224f..ec01a2800dff8 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -107,7 +107,7 @@ import { } from '../common/endpoint/constants'; import { AppFeaturesService } from './lib/app_features_service/app_features_service'; -import { registerRiskScoringTask } from './lib/entity_analytics/risk_engine/tasks/risk_scoring_task'; +import { registerRiskScoringTask } from './lib/entity_analytics/risk_score/tasks/risk_scoring_task'; import { registerProtectionUpdatesNoteRoutes } from './endpoint/routes/protection_updates_note'; import { latestRiskScoreIndexPattern, allRiskScoreIndexPattern } from '../common/risk_engine'; import { isEndpointPackageV2 } from '../common/endpoint/utils/package_v2'; diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index fde473d2a253e..3deaaa103caa6 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -26,6 +26,8 @@ import type { Immutable } from '../common/endpoint/types'; import type { EndpointAuthz } from '../common/endpoint/types/authz'; import type { EndpointAppContextService } from './endpoint/endpoint_app_context_services'; import { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; +import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; +import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality/asset_criticality_data_client'; export interface IRequestContextFactory { create( @@ -141,6 +143,24 @@ export class RequestContextFactory implements IRequestContextFactory { namespace: getSpaceId(), }) ), + getRiskScoreDataClient: memoize( + () => + new RiskScoreDataClient({ + logger: options.logger, + kibanaVersion: options.kibanaVersion, + esClient: coreContext.elasticsearch.client.asCurrentUser, + soClient: coreContext.savedObjects.client, + namespace: getSpaceId(), + }) + ), + getAssetCriticalityDataClient: memoize( + () => + new AssetCriticalityDataClient({ + logger: options.logger, + esClient: coreContext.elasticsearch.client.asCurrentUser, + namespace: getSpaceId(), + }) + ), }; } } diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index b5b6a5c205e95..de1b9d8125f7f 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -29,28 +29,10 @@ import { querySignalsRoute } from '../lib/detection_engine/routes/signals/query_ import { setSignalsStatusRoute } from '../lib/detection_engine/routes/signals/open_close_signals_route'; import { deleteIndexRoute } from '../lib/detection_engine/routes/index/delete_index_route'; import { readPrivilegesRoute } from '../lib/detection_engine/routes/privileges/read_privileges_route'; -import { - createTimelinesRoute, - deleteTimelinesRoute, - exportTimelinesRoute, - getTimelineRoute, - getTimelinesRoute, - importTimelinesRoute, - patchTimelinesRoute, - persistFavoriteRoute, - resolveTimelineRoute, -} from '../lib/timeline/routes/timelines'; -import { getDraftTimelinesRoute } from '../lib/timeline/routes/draft_timelines/get_draft_timelines'; -import { cleanDraftTimelinesRoute } from '../lib/timeline/routes/draft_timelines/clean_draft_timelines'; - -import { persistNoteRoute, deleteNoteRoute } from '../lib/timeline/routes/notes'; - -import { persistPinnedEventRoute } from '../lib/timeline/routes/pinned_events'; import type { SetupPlugins, StartPlugins } from '../plugin'; import type { ConfigType } from '../config'; import type { ITelemetryEventsSender } from '../lib/telemetry/sender'; -import { installPrepackedTimelinesRoute } from '../lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines'; import type { CreateRuleOptions, CreateSecurityRuleTypeWrapperProps, @@ -75,13 +57,16 @@ import { registerDashboardsRoutes } from '../lib/dashboards/routes'; import { registerTagsRoutes } from '../lib/tags/routes'; import { setAlertTagsRoute } from '../lib/detection_engine/routes/signals/set_alert_tags_route'; import { - riskScorePreviewRoute, riskEngineDisableRoute, riskEngineInitRoute, riskEngineEnableRoute, riskEngineStatusRoute, + riskEnginePrivilegesRoute, } from '../lib/entity_analytics/risk_engine/routes'; -import { riskScoreCalculationRoute } from '../lib/entity_analytics/risk_engine/routes/risk_score_calculation_route'; +import { registerTimelineRoutes } from '../lib/timeline/routes'; +import { riskScoreCalculationRoute } from '../lib/entity_analytics/risk_score/routes/calculation'; +import { riskScorePreviewRoute } from '../lib/entity_analytics/risk_score/routes/preview'; +import { assetCriticalityStatusRoute } from '../lib/entity_analytics/asset_criticality/routes'; export const initRoutes = ( router: SecuritySolutionPluginRouter, @@ -120,24 +105,7 @@ export const initRoutes = ( registerResolverRoutes(router, getStartServices, config); - createTimelinesRoute(router, config, security); - patchTimelinesRoute(router, config, security); - - importTimelinesRoute(router, config, security); - exportTimelinesRoute(router, config, security); - getDraftTimelinesRoute(router, config, security); - getTimelineRoute(router, config, security); - resolveTimelineRoute(router, config, security); - getTimelinesRoute(router, config, security); - cleanDraftTimelinesRoute(router, config, security); - deleteTimelinesRoute(router, config, security); - persistFavoriteRoute(router, config, security); - - installPrepackedTimelinesRoute(router, config, security); - - persistNoteRoute(router, config, security); - deleteNoteRoute(router, config, security); - persistPinnedEventRoute(router, config, security); + registerTimelineRoutes(router, config, security); // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals // POST /api/detection_engine/signals/status @@ -187,5 +155,11 @@ export const initRoutes = ( riskEngineInitRoute(router, getStartServices); riskEngineEnableRoute(router, getStartServices); riskEngineDisableRoute(router, getStartServices); + if (config.experimentalFeatures.riskEnginePrivilegesRouteEnabled) { + riskEnginePrivilegesRoute(router, getStartServices); + } + } + if (config.experimentalFeatures.entityAnalyticsAssetCriticalityEnabled) { + assetCriticalityStatusRoute(router, logger); } }; diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index c979cc25c172b..a44572ae07eae 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -30,7 +30,8 @@ import type { FrameworkRequest } from './lib/framework'; import type { EndpointAuthz } from '../common/endpoint/types/authz'; import type { EndpointInternalFleetServicesInterface } from './endpoint/services/fleet'; import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; - +import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; +import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality/asset_criticality_data_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -48,6 +49,8 @@ export interface SecuritySolutionApiRequestHandlerContext { getExceptionListClient: () => ExceptionListClient | null; getInternalFleetServices: () => EndpointInternalFleetServicesInterface; getRiskEngineDataClient: () => RiskEngineDataClient; + getRiskScoreDataClient: () => RiskScoreDataClient; + getAssetCriticalityDataClient: () => AssetCriticalityDataClient; } export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{ diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 4d2b16e355c6c..517e827aaf178 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -15,7 +15,11 @@ "public/**/*.json", "../../../typings/**/*" ], - "exclude": ["target/**/*", "**/cypress/**", "public/management/cypress.config.ts"], + "exclude": [ + "target/**/*", + "**/cypress/**", + "public/management/cypress.config.ts" + ], "kbn_references": [ "@kbn/core", { @@ -149,7 +153,6 @@ "@kbn/analytics-client", "@kbn/security-solution-side-nav", "@kbn/ecs", - "@kbn/url-state", "@kbn/ml-anomaly-utils", "@kbn/discover-plugin", "@kbn/field-formats-plugin", @@ -170,14 +173,15 @@ "@kbn/security-solution-features", "@kbn/content-management-plugin", "@kbn/discover-utils", - "@kbn/subscription-tracking", "@kbn/core-application-common", "@kbn/openapi-generator", + "@kbn/openapi-bundler", "@kbn/es", "@kbn/react-kibana-mount", + "@kbn/react-kibana-context-styled", "@kbn/unified-doc-viewer-plugin", "@kbn/shared-ux-error-boundary", "@kbn/zod-helpers", - "@kbn/core-http-common", + "@kbn/core-http-common" ] } diff --git a/x-pack/plugins/security_solution_ess/public/plugin.ts b/x-pack/plugins/security_solution_ess/public/plugin.ts index 7857c0ffa553d..68b902a95dc84 100644 --- a/x-pack/plugins/security_solution_ess/public/plugin.ts +++ b/x-pack/plugins/security_solution_ess/public/plugin.ts @@ -45,7 +45,7 @@ export class SecuritySolutionEssPlugin }); securitySolution.setComponents({ - getStarted: getSecurityGetStartedComponent(services), + GetStarted: getSecurityGetStartedComponent(services), }); subscribeBreadcrumbs(services); diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.test.ts b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.test.ts index 5b3502225e769..74979b0549b91 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.test.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.test.ts @@ -119,7 +119,7 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLink1.id, title: link1.title, - path: [chromeNavLink1.id], + path: chromeNavLink1.id, deepLink: chromeNavLink1, }, ]); @@ -132,7 +132,7 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLink3.id, title: chromeNavLink3.title, - path: [chromeNavLink3.id], + path: chromeNavLink3.id, deepLink: chromeNavLink3, }, ]); @@ -145,13 +145,13 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLink1.id, title: link1.title, - path: [chromeNavLink1.id], + path: chromeNavLink1.id, deepLink: chromeNavLink1, children: [ { id: chromeNavLink2.id, title: link2.title, - path: [chromeNavLink1.id, chromeNavLink2.id], + path: [chromeNavLink1.id, chromeNavLink2.id].join('.'), deepLink: chromeNavLink2, }, ], @@ -173,24 +173,24 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLinkTest.id, title: link1.title, - path: [chromeNavLinkTest.id], + path: chromeNavLinkTest.id, deepLink: chromeNavLinkTest, children: [ { id: chromeNavLinkMl1.id, title: chromeNavLinkMl1.title, - path: [chromeNavLinkTest.id, chromeNavLinkMl1.id], + path: [chromeNavLinkTest.id, chromeNavLinkMl1.id].join('.'), deepLink: chromeNavLinkMl1, }, { id: defaultNavCategory1.id, title: defaultNavCategory1.title, - path: [chromeNavLinkTest.id, defaultNavCategory1.id], + path: [chromeNavLinkTest.id, defaultNavCategory1.id].join('.'), children: [ { id: chromeNavLinkMl2.id, title: 'Overridden ML SubLink 2', - path: [chromeNavLinkTest.id, defaultNavCategory1.id, chromeNavLinkMl2.id], + path: [chromeNavLinkTest.id, defaultNavCategory1.id, chromeNavLinkMl2.id].join('.'), deepLink: chromeNavLinkMl2, }, ], @@ -208,7 +208,7 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLink2.id, title: link2.title, - path: [chromeNavLink2.id], + path: chromeNavLink2.id, deepLink: chromeNavLink2, }, ]); @@ -230,14 +230,14 @@ describe('formatChromeProjectNavNodes', () => { { id: chromeNavLinkTest.id, title: link1.title, - path: [chromeNavLinkTest.id], + path: chromeNavLinkTest.id, deepLink: chromeNavLinkTest, breadcrumbStatus: 'hidden', }, { id: chromeNavLink2.id, title: link2.title, - path: [chromeNavLink2.id], + path: chromeNavLink2.id, deepLink: chromeNavLink2, }, ]); diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.ts b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.ts index 0dc8a1140aaab..b787aeb927361 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/chrome_navigation_tree.ts @@ -19,7 +19,7 @@ import { isBreadcrumbHidden } from './utils'; export const getFormatChromeProjectNavNodes = (services: Services) => { const formatChromeProjectNavNodes = ( projectNavLinks: ProjectNavigationLink[], - path: string[] = [] + path?: string ): ChromeProjectNavigationNode[] => { const { chrome } = services; @@ -31,7 +31,7 @@ export const getFormatChromeProjectNavNodes = (services: Services) => { const link: ChromeProjectNavigationNode = { id: navLinkId, title, - path: [...path, navLinkId], + path: path ? [path, navLinkId].join('.') : navLinkId, deepLink: chrome.navLinks.get(navLinkId), ...(isBreadcrumbHidden(id) && { breadcrumbStatus: 'hidden' }), }; @@ -63,7 +63,7 @@ export const getFormatChromeProjectNavNodes = (services: Services) => { const processDefaultNav = ( children: NodeDefinition[], - path: string[] + path: string ): ChromeProjectNavigationNode[] => { const { chrome } = services; return children.reduce((navNodes, node) => { @@ -80,7 +80,7 @@ export const getFormatChromeProjectNavNodes = (services: Services) => { const navNode: ChromeProjectNavigationNode = { id, title: node.title || '', - path: [...path, id], + path: [path, id].join('.'), breadcrumbStatus: node.breadcrumbStatus, getIsActive: node.getIsActive, }; diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/project_navigation/project_navigation.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/project_navigation/project_navigation.tsx index b26700eb8e4b3..2532589d1c994 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/project_navigation/project_navigation.tsx +++ b/x-pack/plugins/security_solution_serverless/public/navigation/project_navigation/project_navigation.tsx @@ -13,7 +13,7 @@ import type { import { SolutionSideNavPanelContent } from '@kbn/security-solution-side-nav/panel'; import useObservable from 'react-use/lib/useObservable'; import { useKibana } from '../../common/services'; -import type { ProjectNavigationLink, ProjectPageName } from '../links/types'; +import type { ProjectNavigationLink } from '../links/types'; import { useFormattedSideNavItems } from '../side_navigation/use_side_nav_items'; import { CATEGORIES, FOOTER_CATEGORIES } from '../categories'; import { formatNavigationTree } from '../navigation_tree/navigation_tree'; @@ -21,8 +21,7 @@ import { formatNavigationTree } from '../navigation_tree/navigation_tree'; const getPanelContentProvider = ( projectNavLinks: ProjectNavigationLink[] ): React.FC => - React.memo(function PanelContentProvider({ selectedNode: { path }, closePanel }) { - const linkId = path[path.length - 1] as ProjectPageName; + React.memo(function PanelContentProvider({ selectedNode: { id: linkId }, closePanel }) { const currentPanelItem = projectNavLinks.find((item) => item.id === linkId); const { title = '', links = [], categories } = currentPanelItem ?? {}; diff --git a/x-pack/plugins/security_solution_serverless/public/plugin.ts b/x-pack/plugins/security_solution_serverless/public/plugin.ts index e7e58c42bcaac..77fcbc19d6e26 100644 --- a/x-pack/plugins/security_solution_serverless/public/plugin.ts +++ b/x-pack/plugins/security_solution_serverless/public/plugin.ts @@ -69,8 +69,8 @@ export class SecuritySolutionServerlessPlugin registerUpsellings(securitySolution.getUpselling(), productTypes, services); securitySolution.setComponents({ - getStarted: getSecurityGetStartedComponent(services, productTypes), - dashboardsLandingCallout: getDashboardsLandingCallout(services), + GetStarted: getSecurityGetStartedComponent(services, productTypes), + DashboardsLandingCallout: getDashboardsLandingCallout(services), }); startNavigation(services); diff --git a/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.ts b/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.ts index 1a9501424ba00..0dde94b409875 100644 --- a/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.ts +++ b/x-pack/plugins/security_solution_serverless/server/cloud_security/cloud_security_metering_task.ts @@ -205,14 +205,17 @@ const indexHasDataInDateRange = async ( cloudSecuritySolution: CloudSecuritySolutions, searchFrom: Date ) => { - const response = await esClient.search({ - index: METERING_CONFIGS[cloudSecuritySolution].index, - size: 1, - _source: false, - query: getSearchQueryByCloudSecuritySolution(cloudSecuritySolution, searchFrom), - }); + const response = await esClient.search( + { + index: METERING_CONFIGS[cloudSecuritySolution].index, + size: 1, + _source: false, + query: getSearchQueryByCloudSecuritySolution(cloudSecuritySolution, searchFrom), + }, + { ignore: [404] } + ); - return response.hits.hits.length > 0; + return response.hits?.hits.length > 0; }; const getSearchStartDate = (lastSuccessfulReport: Date): Date => { diff --git a/x-pack/plugins/serverless_search/common/doc_links.ts b/x-pack/plugins/serverless_search/common/doc_links.ts index 6d97c6aed5bed..c3cfecd7dfc66 100644 --- a/x-pack/plugins/serverless_search/common/doc_links.ts +++ b/x-pack/plugins/serverless_search/common/doc_links.ts @@ -18,6 +18,7 @@ class ESDocLinks { public metadata: string = ''; public roleDescriptors: string = ''; public securityApis: string = ''; + public ingestionPipelines: string = ''; // Client links public elasticsearchClients: string = ''; // go @@ -59,6 +60,7 @@ class ESDocLinks { this.metadata = newDocLinks.security.mappingRoles; this.roleDescriptors = newDocLinks.serverlessSecurity.apiKeyPrivileges; this.securityApis = newDocLinks.apis.securityApis; + this.ingestionPipelines = newDocLinks.ingest.pipelines; // Client links this.elasticsearchClients = newDocLinks.serverlessClients.clientLib; diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx index a130e08654bcb..c6ec0469168f3 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/basic_setup_form.tsx @@ -72,7 +72,13 @@ export const BasicSetupForm: React.FC = ({ defaultMessage: 'User', })} > - {}} /> + {}} + /> = ({ }) => { return (
    - + {i18n.translate('xpack.serverlessSearch.apiKey.metadataLinkLabel', { defaultMessage: 'Learn how to structure role metadata', })} diff --git a/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx b/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx index a92224b2b45d6..aefc7971d0a4b 100644 --- a/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/api_key/security_privileges_form.tsx @@ -24,7 +24,11 @@ export const SecurityPrivilegesForm: React.FC = ({ }) => { return (
    - + {i18n.translate('xpack.serverlessSearch.apiKey.roleDescriptorsLinkLabel', { defaultMessage: 'Learn how to structure role descriptors', })} diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/api_key_panel.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/api_key_panel.tsx index dd42310a05cd6..eb1f4aba2ec2d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/api_key_panel.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/api_key_panel.tsx @@ -58,6 +58,7 @@ export const ApiKeyPanel: React.FC = ({ connector }) => { = ({ connecto = ({ connecto = ( - + {i18n.translate('xpack.serverlessSearch.connectors.runWithDockerLink', { defaultMessage: 'Run with Docker', })} @@ -73,6 +79,7 @@ export const ConnectorLinkElasticsearch: React.FC

    - {i18n.translate('xpack.serverlessSearch.connectors.variablesTitle', { - defaultMessage: 'Variables for your ', - })} - elastic/connectors/config.yml + elastic/connectors/config.yml }} + />

    diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/connector_overview.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/connector_overview.tsx index adf6523167739..d141ae51c25ce 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/connector_overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/connector_config/connector_overview.tsx @@ -71,6 +71,7 @@ export const ConnectorOverview: React.FC = ({ connector { name: nameLabel, render: (name: string, connector: Connector) => ( navigateToUrl(generatePath(EDIT_CONNECTOR_PATH, { id: connector.id }))} > {name || connector.id} @@ -235,6 +236,7 @@ export const ConnectorsTable: React.FC = () => {
    setFilter(e.currentTarget.value as Filter)} options={filterOptions} /> @@ -294,6 +296,7 @@ const DeleteConnectorModalAction: React.FC<{ connector: Connector }> = ({ connec )} setModalIsOpen(true)} iconType="trash" diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/delete_connector_modal.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/delete_connector_modal.tsx index 7e9ebb631fc43..a2e4b96176dc0 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/delete_connector_modal.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/delete_connector_modal.tsx @@ -97,6 +97,7 @@ export const DeleteConnectorModal: React.FC = ({ )} > setInputConnectorName(e.target.value)} value={inputConnectorName} /> diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/edit_connector.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/edit_connector.tsx index f979f7dff283d..78d47421420e0 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/edit_connector.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/edit_connector.tsx @@ -72,7 +72,12 @@ export const EditConnector: React.FC = () => { } actions={ - navigateToUrl(`./`)}> + navigateToUrl(`./`)} + > {i18n.translate('xpack.serverlessSearch.connectors.goBack', { defaultMessage: 'Go back', })} @@ -107,6 +112,7 @@ export const EditConnector: React.FC = () => { id={'connectorMenu'} button={ = ({ connector }) = defaultMessage: 'Description', })} labelAppend={ - setIsEditing(true)}> + setIsEditing(true)} + > {EDIT_LABEL} } > {isEditing ? ( setNewDescription(event.target.value)} value={newDescription || ''} /> @@ -102,6 +107,7 @@ export const EditDescription: React.FC = ({ connector }) = `} > mutate(newDescription)} @@ -119,6 +125,7 @@ export const EditDescription: React.FC = ({ connector }) = `} > { diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/edit_name.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/edit_name.tsx index bba3dbec5376a..e8073796c7c9c 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/edit_name.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/edit_name.tsx @@ -80,6 +80,7 @@ export const EditName: React.FC = ({ connector }) => { `} > = ({ connector }) => { defaultMessage: 'Name', })} - setNewName(event.target.value)} value={newName} /> + setNewName(event.target.value)} + value={newName} + /> @@ -108,6 +113,7 @@ export const EditName: React.FC = ({ connector }) => { `} > = ({ connector }) => { `} > { diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx index a997c8086225f..abb0ad64242d7 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors/empty_connectors_prompt.tsx @@ -90,7 +90,10 @@ export const EmptyConnectorsPrompt: React.FC = () => { defaultMessage="Deploy connector code on your own infrastructure by running from {source}, or using {docker}" values={{ source: ( - + {i18n.translate( 'xpack.serverlessSearch.connectorsEmpty.sourceLabel', { defaultMessage: 'source' } @@ -98,7 +101,10 @@ export const EmptyConnectorsPrompt: React.FC = () => { ), docker: ( - + {i18n.translate( 'xpack.serverlessSearch.connectorsEmpty.dockerLabel', { defaultMessage: 'Docker' } @@ -157,7 +163,11 @@ export const EmptyConnectorsPrompt: React.FC = () => {
    - + {i18n.translate('xpack.serverlessSearch.connectorsEmpty.createConnector', { defaultMessage: 'Create connector', })} diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors_ingestion.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors_ingestion.tsx new file mode 100644 index 0000000000000..ede574abba12b --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/connectors_ingestion.tsx @@ -0,0 +1,99 @@ +/* + * Copyright 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 { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiLink, + EuiIcon, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { GithubLink } from '@kbn/search-api-panels'; + +import React from 'react'; +import { useCreateConnector } from '../hooks/api/use_create_connector'; + +export const ConnectorIngestionPanel: React.FC<{ assetBasePath: string }> = ({ assetBasePath }) => { + const { createConnector } = useCreateConnector(); + return ( + + + +
    + {i18n.translate( + 'xpack.serverlessSearch.ingestData.alternativeOptions.connectorsTitle', + { + defaultMessage: 'Connectors', + } + )} +
    +
    + + +

    + {i18n.translate( + 'xpack.serverlessSearch.ingestData.alternativeOptions.connectorsDescription', + { + defaultMessage: + 'Sync third-party data sources to Elasticsearch, by deploying open code Elastic connectors on your own infrastructure. ', + } + )} +

    +
    +
    + + + createConnector()} + > + {i18n.translate( + 'xpack.serverlessSearch.ingestData.alternativeOptions.setupConnectorLabel', + { + defaultMessage: 'Set up a connector', + } + )} + + + + + + + + + + + + + + {i18n.translate( + 'xpack.serverlessSearch.ingestData.alternativeOptions.connectorDockerLabel', + { + defaultMessage: 'Docker', + } + )} + + + + + + + +
    + ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx b/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx index ebd7afc288d27..fd774f3dd4911 100644 --- a/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/connectors_overview.tsx @@ -56,7 +56,11 @@ export const ConnectorsOverview = () => {
    - + {i18n.translate('xpack.serverlessSearch.connectorsPythonLink', { defaultMessage: 'elastic/connectors', })} @@ -67,6 +71,7 @@ export const ConnectorsOverview = () => { { defaultMessage="Sync third-party data sources to Elasticsearch, by deploying Elastic connectors on your own infrastructure. {learnMoreLink}" values={{ learnMoreLink: ( - + {LEARN_MORE_LABEL} ), diff --git a/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx b/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx index fc1699beffa8f..9949b40e3c63f 100644 --- a/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/index_mappings_docs_link.tsx @@ -51,7 +51,12 @@ const IndexMappingsDocsLink: FunctionComponent<{ docLinks: CoreStart['docLinks']

    - + ', () => { const { getByRole } = render(); expect(getByRole('heading', { name: 'Build your first search query' })).toBeDefined(); }); + test('transform data', () => { + const { getByRole } = render(); + expect(getByRole('heading', { name: 'Transform and enrich your data' })).toBeDefined(); + }); test("what's next?", () => { const { getByRole } = render(); expect(getByRole('heading', { name: 'Do more with your data' })).toBeDefined(); diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index cb32a66cea20d..ffabf061dc274 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -13,6 +13,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiLink, EuiPageTemplate, EuiPanel, EuiSpacer, @@ -21,6 +22,7 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { WelcomeBanner, IngestData, @@ -39,8 +41,8 @@ import type { } from '@kbn/search-api-panels'; import { useLocation } from 'react-router-dom'; import { docLinks } from '../../../common/doc_links'; -import { PLUGIN_ID } from '../../../common'; import { useKibanaServices } from '../hooks/use_kibana'; +import { useAssetBasePath } from '../hooks/use_asset_base_path'; import { API_KEY_PLACEHOLDER, CLOUD_ID_PLACEHOLDER, @@ -52,6 +54,9 @@ import { LanguageGrid } from './languages/language_grid'; import './overview.scss'; import { ApiKeyPanel } from './api_key/api_key'; import { ConnectorsCallout } from './connectors_callout'; +import { ConnectorIngestionPanel } from './connectors_ingestion'; +import { PipelineButtonOverview } from './pipeline_button_overview'; +import { PipelinePanel } from './pipeline_panel'; export const ElasticsearchOverview = () => { const [selectedLanguage, setSelectedLanguage] = useState(javaDefinition); @@ -64,7 +69,7 @@ export const ElasticsearchOverview = () => { cloudId: cloud?.cloudId ?? CLOUD_ID_PLACEHOLDER, }; }, [cloud]); - const assetBasePath = http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets`); + const assetBasePath = useAssetBasePath(); const codeSnippetArguments: LanguageDefinitionSnippetArguments = { url: elasticsearchURL, apiKey: clientApiKey, @@ -302,6 +307,7 @@ export const ElasticsearchOverview = () => { docLinks={docLinks} application={application} sharePlugin={share} + additionalIngestionPanel={} /> { })} /> + + + {i18n.translate( + 'xpack.serverlessSearch.pipeline.description.ingestPipelinesLink.link', + { + defaultMessage: 'ingest pipelines', + } + )} + + ), + }} + /> + } + leftPanelContent={} + links={[]} + title={i18n.translate('xpack.serverlessSearch.pipeline.title', { + defaultMessage: 'Transform and enrich your data', + })} + children={} + /> + { {cloud.usersAndRolesUrl && ( - + {i18n.translate('xpack.serverlessSearch.overview.footer.links.inviteUsers', { defaultMessage: 'Invite more users', })} @@ -452,14 +498,22 @@ const OverviewFooter = () => { )} - + {i18n.translate('xpack.serverlessSearch.overview.footer.links.community', { defaultMessage: 'Join our community', })} - + {i18n.translate('xpack.serverlessSearch.overview.footer.links.feedback', { defaultMessage: 'Give feedback', })} diff --git a/x-pack/plugins/serverless_search/public/application/components/pipeline_button_overview.tsx b/x-pack/plugins/serverless_search/public/application/components/pipeline_button_overview.tsx new file mode 100644 index 0000000000000..dd33faad7f6f7 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/pipeline_button_overview.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiSpacer, EuiText, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useKibanaServices } from '../hooks/use_kibana'; + +export const PipelineButtonOverview: React.FC = () => { + const { + application: { navigateToUrl }, + } = useKibanaServices(); + + return ( + <> + + navigateToUrl('/app/management/ingest/ingest_pipelines/create')} + data-test-subj="create-a-pipeline-button" + > + + {i18n.translate('xpack.serverlessSearch.pipeline.description.createButtonLabel', { + defaultMessage: 'Create a pipeline', + })} + + + + ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/pipeline_panel.tsx b/x-pack/plugins/serverless_search/public/application/components/pipeline_panel.tsx new file mode 100644 index 0000000000000..f5b11247b3395 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/application/components/pipeline_panel.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiThemeProvider, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiImage, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useAssetBasePath } from '../hooks/use_asset_base_path'; + +export const PipelinePanel: React.FC = () => { + const assetBasePath = useAssetBasePath(); + + return ( + + + + + + + + + + +

    + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.dataEnrichment.title', + { + defaultMessage: 'Enrich Data', + } + )} +

    +
    + + +

    + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.dataEnrichment.description', + { + defaultMessage: + 'Add information from external sources or apply transformations to your documents for more contextual, insightful search.', + } + )} +

    +
    +
    +
    +
    + + + + + + + +

    + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.extAndStandard.title', + { + defaultMessage: 'Extract and standardize', + } + )} +

    +
    + + + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.extAndStandard.description', + { + defaultMessage: + 'Parse information from your documents to ensure they conform to a standardized format.', + } + )} + +
    +
    +
    + + + + + + + +

    + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.anonymization.title', + { + defaultMessage: 'Anonymize data', + } + )} +

    +
    + + + {i18n.translate( + 'xpack.serverlessSearch.pipeline.overview.anonymization.description', + { + defaultMessage: + 'Remove sensitive information from documents before indexing.', + } + )} + +
    +
    +
    +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/serverless_search/public/assets/cluster.svg b/x-pack/plugins/serverless_search/public/assets/cluster.svg new file mode 100644 index 0000000000000..99d4d0a731377 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/assets/cluster.svg @@ -0,0 +1,4 @@ + + + + diff --git a/x-pack/plugins/serverless_search/public/assets/cut.svg b/x-pack/plugins/serverless_search/public/assets/cut.svg new file mode 100644 index 0000000000000..58d33d453d716 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/assets/cut.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/serverless_search/public/assets/reporter.svg b/x-pack/plugins/serverless_search/public/assets/reporter.svg new file mode 100644 index 0000000000000..bfd2a2afe869c --- /dev/null +++ b/x-pack/plugins/serverless_search/public/assets/reporter.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts b/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts index 10f1c48977c34..ff76314776025 100644 --- a/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts +++ b/x-pack/plugins/session_view/server/routes/alerts_client_mock.test.ts @@ -65,6 +65,7 @@ const alertsClientParams: jest.Mocked = { ruleDataService: ruleDataServiceMock.create(), esClient: esClientMock, getRuleType: jest.fn(), + getRuleList: jest.fn(), getAlertIndicesAlias: getAlertIndicesAliasMock, }; diff --git a/x-pack/plugins/stack_alerts/public/rule_types/components/index_select_popover.tsx b/x-pack/plugins/stack_alerts/public/rule_types/components/index_select_popover.tsx index c3923e08bbd78..84017dd96bbae 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/components/index_select_popover.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/components/index_select_popover.tsx @@ -108,7 +108,7 @@ export const IndexSelectPopover: React.FunctionComponent = ({ index && index.length > 0 ? renderIndices(index) : i18n.translate('xpack.stackAlerts.components.ui.alertParams.indexPlaceholder', { - defaultMessage: 'Select an index', + defaultMessage: 'Select indices and a time field', }) } isActive={indexPopoverOpen} diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx index 7a9bb590af73b..f1e91ba63dc69 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx @@ -197,7 +197,7 @@ export const EsQueryExpression: React.FC<
    diff --git a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx index be3f1b6f25c99..5fc0d89216292 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx @@ -167,7 +167,7 @@ export const IndexThresholdRuleTypeExpression: React.FunctionComponent<
    @@ -203,6 +203,16 @@ export const IndexThresholdRuleTypeExpression: React.FunctionComponent< setRuleParams('timeField', updatedTimeField) } /> + + +
    + +
    +
    + setRuleParams('termSize', selectedTermSize)} /> - - -
    - -
    -
    - - - -
    - -
    -
    + + + } helpText={i18n.translate('xpack.stackAlerts.threshold.ui.filterKQLHelpText', { defaultMessage: 'Use a KQL expression to limit the scope of your alerts.', })} diff --git a/x-pack/plugins/stack_connectors/common/experimental_features.ts b/x-pack/plugins/stack_connectors/common/experimental_features.ts index b7f1fe2c1b87b..4ac02dd9f06db 100644 --- a/x-pack/plugins/stack_connectors/common/experimental_features.ts +++ b/x-pack/plugins/stack_connectors/common/experimental_features.ts @@ -12,7 +12,7 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; * This object is then used to validate and parse the value entered. */ export const allowedExperimentalValues = Object.freeze({ - isMustacheAutocompleteOn: true, + isMustacheAutocompleteOn: false, sentinelOneConnectorOn: false, }); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.test.tsx new file mode 100644 index 0000000000000..10c78d4029d9a --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.test.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 { screen, render } from '@testing-library/react'; +import { LinksList } from './links_list'; +import userEvent from '@testing-library/user-event'; + +describe('LinksList', () => { + const editAction = jest.fn(); + + const options = { + index: 0, + errors: { + links: [], + }, + editAction, + links: [], + }; + + beforeEach(() => jest.clearAllMocks()); + + it('the list is empty by default', () => { + render(); + + expect(screen.queryByTestId('linksListItemRow')).not.toBeInTheDocument(); + }); + + it('clicking add button calls editAction with correct params', async () => { + render(); + + userEvent.click(await screen.findByTestId('pagerDutyAddLinkButton')); + + expect(editAction).toHaveBeenCalledWith('links', [{ href: '', text: '' }], 0); + }); + + it('clicking remove link button calls editAction with correct params', async () => { + render( + + ); + + expect(await screen.findAllByTestId('linksListItemRow', { exact: false })).toHaveLength(3); + + userEvent.click((await screen.findAllByTestId('pagerDutyRemoveLinkButton'))[1]); + + expect(editAction).toHaveBeenCalledWith( + 'links', + [ + { href: '1', text: 'foobar' }, + { href: '3', text: 'foobar' }, + ], + 0 + ); + }); + + it('editing a link href field calls editAction with correct params', async () => { + render(); + + expect(await screen.findByTestId('linksListItemRow', { exact: false })).toBeInTheDocument(); + + userEvent.paste(await screen.findByTestId('linksHrefInput'), 'newHref'); + + expect(editAction).toHaveBeenCalledWith('links', [{ href: 'newHref', text: 'foobar' }], 0); + }); + + it('editing a link text field calls editAction with correct params', async () => { + render(); + + expect(await screen.findByTestId('linksListItemRow', { exact: false })).toBeInTheDocument(); + + userEvent.paste(await screen.findByTestId('linksTextInput'), 'newText'); + + expect(editAction).toHaveBeenCalledWith('links', [{ href: 'foobar', text: 'newText' }], 0); + }); + + it('correctly displays error messages', async () => { + render(); + + expect(await screen.findByText('FoobarError')); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.tsx new file mode 100644 index 0000000000000..70477fc03683a --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/links_list.tsx @@ -0,0 +1,138 @@ +/* + * Copyright 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 { + EuiButton, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + ActionParamsProps, + TextFieldWithMessageVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { PagerDutyActionParams } from '../types'; + +type LinksListProps = Pick< + ActionParamsProps, + 'index' | 'editAction' | 'errors' | 'messageVariables' +> & + Pick; + +export const LinksList: React.FC = ({ + editAction, + errors, + index, + links, + messageVariables, +}) => { + const areLinksInvalid = Array.isArray(errors.links) && errors.links.length > 0; + + return ( + + + {links && + links.map((link, currentLinkIndex) => ( + + + + + + { + const newLinks = [...links]; + newLinks[currentLinkIndex] = { text: link.text, href: value }; + editAction('links', newLinks, actionIndex); + }} + messageVariables={messageVariables} + paramsProperty={'linksHref'} + inputTargetValue={link.href} + /> + + + + + { + const newLinks = [...links]; + newLinks[currentLinkIndex] = { href: link.href, text: value }; + editAction('links', newLinks, actionIndex); + }} + messageVariables={messageVariables} + paramsProperty={'linksText'} + inputTargetValue={link.text} + /> + + + + { + links.splice(currentLinkIndex, 1); + editAction('links', links, index); + }} + iconType="minusInCircle" + css={{ marginTop: 28 }} + data-test-subj="pagerDutyRemoveLinkButton" + /> + + + + ))} + +
    + + editAction( + 'links', + links ? [...links, { href: '', text: '' }] : [{ href: '', text: '' }], + index + ) + } + data-test-subj="pagerDutyAddLinkButton" + > + {i18n.translate('xpack.stackConnectors.components.pagerDuty.addLinkButtonLabel', { + defaultMessage: 'Add Link', + })} + +
    +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.test.tsx index 311d4bfac8679..4df68dee15880 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.test.tsx @@ -43,6 +43,8 @@ describe('pagerduty action params validation', () => { component: 'test', group: 'group', class: 'test class', + customDetails: '{}', + links: [], }; expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ @@ -50,6 +52,8 @@ describe('pagerduty action params validation', () => { dedupKey: [], summary: [], timestamp: [], + links: [], + customDetails: [], }, }); }); @@ -74,6 +78,142 @@ describe('pagerduty action params validation', () => { dedupKey: [], summary: [], timestamp: expect.arrayContaining(expected), + links: [], + customDetails: [], + }, + }); + }); + + test('action params validation fails when customDetails are not valid JSON', async () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + customDetails: '{foo:bar}', + links: [], + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + dedupKey: [], + summary: [], + timestamp: [], + links: [], + customDetails: ['Custom details must be a valid JSON.'], + }, + }); + }); + + test('action params validation does not fail when customDetails are not JSON but have mustache templates inside', async () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + customDetails: '{"details": {{alert.flapping}}}', + links: [], + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + dedupKey: [], + summary: [], + timestamp: [], + links: [], + customDetails: [], + }, + }); + }); + + test('action params validation fails when a link is missing the href field', async () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + customDetails: '{}', + links: [{ href: '', text: 'foobar' }], + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + dedupKey: [], + summary: [], + timestamp: [], + links: ['Link properties cannot be empty.'], + customDetails: [], + }, + }); + }); + + test('action params validation fails when a link is missing the text field', async () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + customDetails: '{}', + links: [{ href: 'foobar', text: '' }], + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + dedupKey: [], + summary: [], + timestamp: [], + links: ['Link properties cannot be empty.'], + customDetails: [], + }, + }); + }); + + test('action params validation does not throw the same error multiple times for links', async () => { + const actionParams = { + eventAction: 'trigger', + dedupKey: 'test', + summary: '2323', + source: 'source', + severity: 'critical', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + customDetails: '{}', + links: [ + { href: 'foobar', text: '' }, + { href: '', text: 'foobar' }, + { href: '', text: '' }, + ], + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + dedupKey: [], + summary: [], + timestamp: [], + links: ['Link properties cannot be empty.'], + customDetails: [], }, }); }); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.tsx index 0dd1513ac55ac..b02665eec66be 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty.tsx @@ -50,6 +50,8 @@ export function getConnectorType(): ConnectorTypeModel< summary: new Array(), timestamp: new Array(), dedupKey: new Array(), + links: new Array(), + customDetails: new Array(), }; const validationResult = { errors }; if ( @@ -79,6 +81,31 @@ export function getConnectorType(): ConnectorTypeModel< ); } } + if (Array.isArray(actionParams.links)) { + actionParams.links.forEach(({ href, text }) => { + if ((!href || !text) && errors.links.length === 0) { + errors.links.push( + i18n.translate('xpack.stackConnectors.components.pagerDuty.error.invalidLink', { + defaultMessage: 'Link properties cannot be empty.', + }) + ); + } + }); + } + if (actionParams.customDetails?.length && !hasMustacheTokens(actionParams.customDetails)) { + try { + JSON.parse(actionParams.customDetails); + } catch { + errors.customDetails.push( + i18n.translate( + 'xpack.stackConnectors.components.pagerDuty.error.invalidCustomDetails', + { + defaultMessage: 'Custom details must be a valid JSON.', + } + ) + ); + } + } return validationResult; }, actionConnectorFields: lazy(() => import('./pagerduty_connectors')), diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.test.tsx index 19f47166b2726..aa1e6be9bebe0 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.test.tsx @@ -11,7 +11,7 @@ import { EventActionOptions, SeverityActionOptions } from '../types'; import PagerDutyParamsFields from './pagerduty_params'; describe('PagerDutyParamsFields renders', () => { - test('all params fields is rendered', () => { + test('all params fields are rendered', () => { const actionParams = { eventAction: EventActionOptions.TRIGGER, dedupKey: 'test', @@ -22,6 +22,11 @@ describe('PagerDutyParamsFields renders', () => { component: 'test', group: 'group', class: 'test class', + customDetails: '{"foo":"bar"}', + links: [ + { href: 'foo', text: 'bar' }, + { href: 'foo', text: 'bar' }, + ], }; const wrapper = mountWithIntl( @@ -55,6 +60,9 @@ describe('PagerDutyParamsFields renders', () => { expect(wrapper.find('[data-test-subj="sourceInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="summaryInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="dedupKeyAddVariableButton"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="customDetailsJsonEditor"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="linksList"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="pagerDutyAddLinkButton"]').length > 0).toBeTruthy(); }); test('params select fields do not auto set values eventActionSelect', () => { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.tsx index 970ef6ae1ecbf..8505adfee17f8 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/pagerduty/pagerduty_params.tsx @@ -6,12 +6,23 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSelect, + EuiSpacer, + useEuiTheme, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isUndefined } from 'lodash'; -import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + ActionParamsProps, + JsonEditorWithMessageVariables, +} from '@kbn/triggers-actions-ui-plugin/public'; import { TextFieldWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; import { PagerDutyActionParams } from '../types'; +import { LinksList } from './links_list'; const PagerDutyParamsFields: React.FunctionComponent> = ({ actionParams, @@ -20,8 +31,20 @@ const PagerDutyParamsFields: React.FunctionComponent { - const { eventAction, dedupKey, summary, source, severity, timestamp, component, group } = - actionParams; + const { euiTheme } = useEuiTheme(); + + const { + eventAction, + dedupKey, + summary, + source, + severity, + timestamp, + component, + group, + customDetails, + links, + } = actionParams; const severityOptions = [ { value: 'critical', @@ -125,7 +148,7 @@ const PagerDutyParamsFields: React.FunctionComponent
    - + - {isTriggerPagerDutyEvent ? ( + {isTriggerPagerDutyEvent && ( <> + + { + editAction('customDetails', json, index); + }} + onBlur={() => { + if (!customDetails) { + editAction('customDetails', '', index); + } + }} + data-test-subj="customDetailsJsonEditor" + /> + + - ) : null} + )} ); }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/types.ts index 72319df375e1f..eb0cb9927dce1 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/types.ts @@ -39,6 +39,8 @@ export interface PagerDutyActionParams { component?: string; group?: string; class?: string; + customDetails?: string; + links?: Array<{ href: string; text: string }>; } export interface IndexActionParams { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.test.ts index 708e8cd4e0364..0eeb309dd2257 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.test.ts @@ -5,13 +5,10 @@ * 2.0. */ import aws from 'aws4'; -import { Transform } from 'stream'; +import { PassThrough, Transform } from 'stream'; import { BedrockConnector } from './bedrock'; -import { waitFor } from '@testing-library/react'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; -import { EventStreamCodec } from '@smithy/eventstream-codec'; -import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; import { RunActionResponseSchema, StreamingResponseSchema } from '../../../common/bedrock/schema'; import { @@ -105,7 +102,7 @@ describe('BedrockConnector', () => { let stream; beforeEach(() => { stream = createStreamMock(); - stream.write(encodeBedrockResponse(mockResponseString)); + stream.write(new Uint8Array([1, 2, 3])); mockRequest = jest.fn().mockResolvedValue({ ...mockResponse, data: stream.transform }); // @ts-ignore connector.request = mockRequest; @@ -199,16 +196,9 @@ describe('BedrockConnector', () => { }); }); - it('transforms the response into a string', async () => { + it('responds with a readable stream', async () => { const response = await connector.invokeStream(aiAssistantBody); - - let responseBody: string = ''; - response.on('data', (data: string) => { - responseBody += data.toString(); - }); - await waitFor(() => { - expect(responseBody).toEqual(mockResponseString); - }); + expect(response instanceof PassThrough).toEqual(true); }); it('errors during API calls are properly handled', async () => { @@ -364,16 +354,3 @@ function createStreamMock() { }, }; } - -function encodeBedrockResponse(completion: string) { - return new EventStreamCodec(toUtf8, fromUtf8).encode({ - headers: {}, - body: Uint8Array.from( - Buffer.from( - JSON.stringify({ - bytes: Buffer.from(JSON.stringify({ completion })).toString('base64'), - }) - ) - ), - }); -} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts index 70f8e121e1519..ade589e54dc14 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts @@ -9,9 +9,7 @@ import { ServiceParams, SubActionConnector } from '@kbn/actions-plugin/server'; import aws from 'aws4'; import type { AxiosError } from 'axios'; import { IncomingMessage } from 'http'; -import { PassThrough, Transform } from 'stream'; -import { EventStreamCodec } from '@smithy/eventstream-codec'; -import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; +import { PassThrough } from 'stream'; import { RunActionParamsSchema, RunActionResponseSchema, @@ -178,12 +176,12 @@ export class BedrockConnector extends SubActionConnector { * @param messages An array of messages to be sent to the API * @param model Optional model to be used for the API request. If not provided, the default model from the connector will be used. */ - public async invokeStream({ messages, model }: InvokeAIActionParams): Promise { + public async invokeStream({ messages, model }: InvokeAIActionParams): Promise { const res = (await this.streamApi({ body: JSON.stringify(formatBedrockBody({ messages })), model, })) as unknown as IncomingMessage; - return res.pipe(transformToString()); + return res; } /** @@ -222,25 +220,3 @@ const formatBedrockBody = ({ stop_sequences: ['\n\nHuman:'], }; }; - -/** - * Takes in a readable stream of data and returns a Transform stream that - * uses the AWS proprietary codec to parse the proprietary bedrock response into - * a string of the response text alone, returning the response string to the stream - */ -const transformToString = () => - new Transform({ - transform(chunk, encoding, callback) { - const encoder = new TextEncoder(); - const decoder = new EventStreamCodec(toUtf8, fromUtf8); - const event = decoder.decode(chunk); - const body = JSON.parse( - Buffer.from( - JSON.parse(new TextDecoder('utf-8').decode(event.body)).bytes, - 'base64' - ).toString() - ); - const newChunk = encoder.encode(body.completion); - callback(null, newChunk); - }, - }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts index 7769dd8592faf..c7d6feb6887ad 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts @@ -17,8 +17,7 @@ import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; import { RunActionResponseSchema, StreamingResponseSchema } from '../../../common/openai/schema'; import { initDashboard } from './create_dashboard'; -import { Transform } from 'stream'; -import { waitFor } from '@testing-library/react'; +import { PassThrough, Transform } from 'stream'; jest.mock('./create_dashboard'); describe('OpenAIConnector', () => { @@ -315,53 +314,11 @@ describe('OpenAIConnector', () => { await expect(connector.invokeStream(sampleOpenAiBody)).rejects.toThrow('API Error'); }); - it('transforms the response into a string', async () => { + it('responds with a readable stream', async () => { // @ts-ignore connector.request = mockStream(); const response = await connector.invokeStream(sampleOpenAiBody); - - let responseBody: string = ''; - response.on('data', (data: string) => { - responseBody += data.toString(); - }); - await waitFor(() => { - expect(responseBody).toEqual('My new'); - }); - }); - it('correctly buffers stream of json lines', async () => { - const chunk1 = `data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"}}]}`; - const chunk2 = `\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]`; - - // @ts-ignore - connector.request = mockStream([chunk1, chunk2]); - - const response = await connector.invokeStream(sampleOpenAiBody); - - let responseBody: string = ''; - response.on('data', (data: string) => { - responseBody += data.toString(); - }); - await waitFor(() => { - expect(responseBody).toEqual('My new message'); - }); - }); - it('correctly buffers partial lines', async () => { - const chunk1 = `data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"`; - - const chunk2 = `}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" message"}}]}\ndata: [DONE]`; - - // @ts-ignore - connector.request = mockStream([chunk1, chunk2]); - - const response = await connector.invokeStream(sampleOpenAiBody); - - let responseBody: string = ''; - response.on('data', (data: string) => { - responseBody += data.toString(); - }); - await waitFor(() => { - expect(responseBody).toEqual('My new message'); - }); + expect(response instanceof PassThrough).toEqual(true); }); }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts index 78fca4bd84198..8dfeac0be8502 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts @@ -7,8 +7,8 @@ import { ServiceParams, SubActionConnector } from '@kbn/actions-plugin/server'; import type { AxiosError } from 'axios'; -import { PassThrough, Transform } from 'stream'; import { IncomingMessage } from 'http'; +import { PassThrough } from 'stream'; import { RunActionParamsSchema, RunActionResponseSchema, @@ -198,13 +198,13 @@ export class OpenAIConnector extends SubActionConnector { * the response from the streamApi method and returns the response string alone. * @param body - the OpenAI Invoke request body */ - public async invokeStream(body: InvokeAIActionParams): Promise { + public async invokeStream(body: InvokeAIActionParams): Promise { const res = (await this.streamApi({ body: JSON.stringify(body), stream: true, })) as unknown as IncomingMessage; - return res.pipe(new PassThrough()).pipe(transformToString()); + return res.pipe(new PassThrough()); } /** @@ -229,44 +229,3 @@ export class OpenAIConnector extends SubActionConnector { }; } } - -/** - * Takes in a readable stream of data and returns a Transform stream that - * parses the proprietary OpenAI response into a string of the response text alone, - * returning the response string to the stream - */ -const transformToString = () => { - let lineBuffer: string = ''; - const decoder = new TextDecoder(); - - return new Transform({ - transform(chunk, encoding, callback) { - const chunks = decoder.decode(chunk); - const lines = chunks.split('\n'); - lines[0] = lineBuffer + lines[0]; - lineBuffer = lines.pop() || ''; - callback(null, getNextChunk(lines)); - }, - flush(callback) { - // Emit an additional chunk with the content of lineBuffer if it has length - if (lineBuffer.length > 0) { - callback(null, getNextChunk([lineBuffer])); - } else { - callback(); - } - }, - }); -}; - -const getNextChunk = (lines: string[]) => { - const encoder = new TextEncoder(); - const nextChunk = lines - .map((str) => str.substring(6)) - .filter((str) => !!str && str !== '[DONE]') - .map((line) => { - const openaiResponse = JSON.parse(line); - return openaiResponse.choices[0]?.delta.content ?? ''; - }) - .join(''); - return encoder.encode(nextChunk); -}; diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index e722801c12c1d..b46e839807be2 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -10,7 +10,7 @@ import { BrowserAdvancedFields, BrowserSimpleFields, CommonFields, - DataStream, + MonitorTypeEnum, FormMonitorType, HTTPAdvancedFields, HTTPMethod, @@ -129,7 +129,7 @@ export const ALLOWED_SCHEDULES_IN_MINUTES = [ ]; export const DEFAULT_COMMON_FIELDS: CommonFields = { - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, [ConfigKey.ENABLED]: true, [ConfigKey.ALERT_CONFIG]: { status: { enabled: true }, tls: { enabled: true } }, @@ -171,7 +171,7 @@ export const DEFAULT_BROWSER_SIMPLE_FIELDS: BrowserSimpleFields = { file_name: '', }, }, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.BROWSER, [ConfigKey.PORT]: null, [ConfigKey.SCHEDULE]: { unit: ScheduleUnit.MINUTES, @@ -192,7 +192,7 @@ export const DEFAULT_HTTP_SIMPLE_FIELDS: HTTPSimpleFields = { }, [ConfigKey.URLS]: '', [ConfigKey.MAX_REDIRECTS]: '0', - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, [ConfigKey.PORT]: null, }; @@ -224,7 +224,7 @@ export const DEFAULT_HTTP_ADVANCED_FIELDS: HTTPAdvancedFields = { export const DEFAULT_ICMP_SIMPLE_FIELDS: ICMPSimpleFields = { ...DEFAULT_COMMON_FIELDS, [ConfigKey.HOSTS]: '', - [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.ICMP, [ConfigKey.WAIT]: '1', [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.ICMP, }; @@ -236,7 +236,7 @@ export const DEFAULT_TCP_SIMPLE_FIELDS: TCPSimpleFields = { }, [ConfigKey.HOSTS]: '', [ConfigKey.URLS]: '', - [ConfigKey.MONITOR_TYPE]: DataStream.TCP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.TCP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, [ConfigKey.PORT]: null, }; @@ -267,21 +267,21 @@ export const DEFAULT_TLS_FIELDS: TLSFields = { }; export const DEFAULT_FIELDS: MonitorDefaults = { - [DataStream.HTTP]: { + [MonitorTypeEnum.HTTP]: { ...DEFAULT_HTTP_SIMPLE_FIELDS, ...DEFAULT_HTTP_ADVANCED_FIELDS, ...DEFAULT_TLS_FIELDS, }, - [DataStream.TCP]: { + [MonitorTypeEnum.TCP]: { ...DEFAULT_TCP_SIMPLE_FIELDS, ...DEFAULT_TCP_ADVANCED_FIELDS, ...DEFAULT_TLS_FIELDS, }, - [DataStream.ICMP]: { + [MonitorTypeEnum.ICMP]: { ...DEFAULT_ICMP_SIMPLE_FIELDS, ...DEFAULT_ICMP_ADVANCED_FIELDS, }, - [DataStream.BROWSER]: { + [MonitorTypeEnum.BROWSER]: { ...DEFAULT_BROWSER_SIMPLE_FIELDS, ...DEFAULT_BROWSER_ADVANCED_FIELDS, ...DEFAULT_TLS_FIELDS, diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts index 5cbf2efbe62f7..428f8ffe043b2 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_configs.ts @@ -8,15 +8,14 @@ import * as t from 'io-ts'; import { tEnum } from '../../utils/t_enum'; -export enum DataStream { +export enum MonitorTypeEnum { HTTP = 'http', TCP = 'tcp', ICMP = 'icmp', BROWSER = 'browser', } -export const DataStreamCodec = tEnum('DataStream', DataStream); -export type DataStreamType = t.TypeOf; +export const MonitorTypeCodec = tEnum('MonitorType', MonitorTypeEnum); export enum HTTPMethod { GET = 'GET', @@ -26,9 +25,6 @@ export enum HTTPMethod { HEAD = 'HEAD', } -export const HTTPMethodCodec = tEnum('HTTPMethod', HTTPMethod); -export type HTTPMethodType = t.TypeOf; - export enum ResponseBodyIndexPolicy { ALWAYS = 'always', NEVER = 'never', @@ -39,7 +35,6 @@ export const ResponseBodyIndexPolicyCodec = tEnum( 'ResponseBodyIndexPolicy', ResponseBodyIndexPolicy ); -export type ResponseBodyIndexPolicyType = t.TypeOf; export enum MonacoEditorLangId { JSON = 'xjson', @@ -48,12 +43,6 @@ export enum MonacoEditorLangId { JAVASCRIPT = 'javascript', } -export const MonacoEditorLangIdCodec = tEnum( - 'MonacoEditorLangId', - MonacoEditorLangId -); -export type MonacoEditorLangIdType = t.TypeOf; - export enum CodeEditorMode { FORM = 'form', JSON = 'json', @@ -62,7 +51,6 @@ export enum CodeEditorMode { } export const CodeEditorModeCodec = tEnum('CodeEditorMode', CodeEditorMode); -export type CodeEditorModeType = t.TypeOf; export enum ContentType { JSON = 'application/json', @@ -71,16 +59,12 @@ export enum ContentType { FORM = 'application/x-www-form-urlencoded', } -export const ContentTypeCodec = tEnum('ContentType', ContentType); -export type ContentTypeType = t.TypeOf; - export enum ScheduleUnit { MINUTES = 'm', SECONDS = 's', } export const ScheduleUnitCodec = tEnum('ScheduleUnit', ScheduleUnit); -export type ScheduleUnitType = t.TypeOf; export enum VerificationMode { CERTIFICATE = 'certificate', @@ -90,7 +74,6 @@ export enum VerificationMode { } export const VerificationModeCodec = tEnum('VerificationMode', VerificationMode); -export type VerificationModeType = t.TypeOf; export enum TLSVersion { ONE_ZERO = 'TLSv1.0', @@ -100,7 +83,6 @@ export enum TLSVersion { } export const TLSVersionCodec = tEnum('TLSVersion', TLSVersion); -export type TLSVersionType = t.TypeOf; export enum ScreenshotOption { ON = 'on', @@ -109,7 +91,6 @@ export enum ScreenshotOption { } export const ScreenshotOptionCodec = tEnum('ScreenshotOption', ScreenshotOption); -export type ScreenshotOptionType = t.TypeOf; export enum SourceType { UI = 'ui', @@ -133,7 +114,6 @@ export enum Mode { ALL = 'all', } export const ModeCodec = tEnum('Mode', Mode); -export type ModeType = t.TypeOf; export const ResponseCheckJSONCodec = t.interface({ description: t.string, diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 27c55ade654c2..d5cae36999aa9 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -11,8 +11,8 @@ import { secretKeys } from '../../constants/monitor_management'; import { ConfigKey } from './config_key'; import { MonitorServiceLocationCodec, ServiceLocationErrors } from './locations'; import { - DataStream, - DataStreamCodec, + MonitorTypeEnum, + MonitorTypeCodec, FormMonitorTypeCodec, ModeCodec, ResponseBodyIndexPolicyCodec, @@ -55,7 +55,7 @@ export const CommonFieldsCodec = t.intersection([ t.interface({ [ConfigKey.NAME]: t.string, [ConfigKey.NAMESPACE]: t.string, - [ConfigKey.MONITOR_TYPE]: DataStreamCodec, + [ConfigKey.MONITOR_TYPE]: MonitorTypeCodec, [ConfigKey.ENABLED]: t.boolean, [ConfigKey.SCHEDULE]: ScheduleCodec, [ConfigKey.APM_SERVICE_NAME]: t.string, @@ -350,10 +350,10 @@ export const EncryptedSyntheticsSavedMonitorCodec = t.intersection([ ]); export const MonitorDefaultsCodec = t.interface({ - [DataStream.HTTP]: HTTPFieldsCodec, - [DataStream.TCP]: TCPFieldsCodec, - [DataStream.ICMP]: ICMPSimpleFieldsCodec, - [DataStream.BROWSER]: BrowserFieldsCodec, + [MonitorTypeEnum.HTTP]: HTTPFieldsCodec, + [MonitorTypeEnum.TCP]: TCPFieldsCodec, + [MonitorTypeEnum.ICMP]: ICMPSimpleFieldsCodec, + [MonitorTypeEnum.BROWSER]: BrowserFieldsCodec, }); export const MonitorManagementListResultCodec = t.type({ diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx index 32b930ddfee2f..6fa6bf7d320ee 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_inspect.tsx @@ -27,7 +27,7 @@ import { import { ClientPluginsStart } from '../../../../../plugin'; import { useSyntheticsSettingsContext } from '../../../contexts'; import { LoadingState } from '../../monitors_page/overview/overview/monitor_detail_flyout'; -import { DataStream, MonitorFields } from '../../../../../../common/runtime_types'; +import { MonitorTypeEnum, MonitorFields } from '../../../../../../common/runtime_types'; import { inspectMonitorAPI, MonitorInspectResponse } from '../../../state/monitor_management/api'; interface InspectorProps { @@ -159,7 +159,7 @@ const formatContent = (result: MonitorInspectResponse) => { const currentInput = result.privateConfig?.inputs.find((input) => input.enabled); const compiledConfig = currentInput?.streams.find((stream) => - Object.values(DataStream).includes(stream.data_stream.dataset as DataStream) + Object.values(MonitorTypeEnum).includes(stream.data_stream.dataset as MonitorTypeEnum) )?.compiled_stream; return JSON.stringify( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_type_badge.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_type_badge.tsx index 5664d30761a96..49c7bde90de50 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_type_badge.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_type_badge.tsx @@ -12,7 +12,7 @@ import { EncryptedSyntheticsMonitor, ConfigKey, FormMonitorType, - DataStream, + MonitorTypeEnum, } from '../../../../../../common/runtime_types'; export function MonitorTypeBadge({ @@ -55,7 +55,7 @@ function getMonitorTypeBadgeTitle(monitor: EncryptedSyntheticsMonitor) { } switch (monitor?.type) { - case DataStream.BROWSER: + case MonitorTypeEnum.BROWSER: return 'Journey'; default: return monitor?.type?.toUpperCase(); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts index 070bb39d9d96d..d014891f53465 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts @@ -16,7 +16,7 @@ import { createGettingStartedMonitor, UpsertMonitorResponse } from '../../state' import { DEFAULT_FIELDS } from '../../../../../common/constants/monitor_defaults'; import { ConfigKey } from '../../../../../common/constants/monitor_management'; import { - DataStream, + MonitorTypeEnum, EncryptedSyntheticsSavedMonitor, ServiceLocationErrors, } from '../../../../../common/runtime_types'; @@ -47,7 +47,7 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData 'source.inline.script': `step('Go to ${urls}', async () => { await page.goto('${urls}'); });`, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.BROWSER, [ConfigKey.NAME]: urls, [ConfigKey.LOCATIONS]: locations, [ConfigKey.URLS]: urls, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx index 90652b609db6f..f4d2b3d0e3edd 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ConfigKey, DataStream, FormMonitorType, SyntheticsMonitor } from '../types'; +import { ConfigKey, MonitorTypeEnum, FormMonitorType, SyntheticsMonitor } from '../types'; import { DEFAULT_FIELDS, PROFILE_VALUES_ENUM, PROFILES_MAP } from '../constants'; import { formatDefaultFormValues } from './defaults'; @@ -128,13 +128,13 @@ describe('defaults', () => { }); it.each([ - [DataStream.HTTP, true], - [DataStream.HTTP, false], - [DataStream.TCP, true], - [DataStream.TCP, false], + [MonitorTypeEnum.HTTP, true], + [MonitorTypeEnum.HTTP, false], + [MonitorTypeEnum.TCP, true], + [MonitorTypeEnum.TCP, false], ])('correctly formats isTLSEnabled', (formType, isTLSEnabled) => { const monitor = { - ...DEFAULT_FIELDS[formType as DataStream], + ...DEFAULT_FIELDS[formType as MonitorTypeEnum], [ConfigKey.FORM_MONITOR_TYPE]: formType as unknown as FormMonitorType, [ConfigKey.TLS_CERTIFICATE_AUTHORITIES]: 'mockCA', [ConfigKey.METADATA]: { @@ -149,10 +149,10 @@ describe('defaults', () => { }); it.each([ - [DataStream.HTTP, FormMonitorType.HTTP], - [DataStream.TCP, FormMonitorType.TCP], - [DataStream.ICMP, FormMonitorType.ICMP], - [DataStream.BROWSER, FormMonitorType.MULTISTEP], + [MonitorTypeEnum.HTTP, FormMonitorType.HTTP], + [MonitorTypeEnum.TCP, FormMonitorType.TCP], + [MonitorTypeEnum.ICMP, FormMonitorType.ICMP], + [MonitorTypeEnum.BROWSER, FormMonitorType.MULTISTEP], ])( 'correctly formats legacy uptime monitors to include ConfigKey.FORM_MONITOR_TYPE', (dataStream, formType) => { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.tsx index ba63d7c1d75ad..f24117f101666 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.tsx @@ -9,7 +9,7 @@ import { DEFAULT_FIELDS } from '../constants'; import { ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, SyntheticsMonitor, BrowserFields, @@ -22,7 +22,7 @@ export const getDefaultFormFields = ( const kibanaNamespace = formatKibanaNamespace(spaceId); return { [FormMonitorType.MULTISTEP]: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], 'source.inline': { type: 'recorder', script: '', @@ -32,24 +32,24 @@ export const getDefaultFormFields = ( [ConfigKey.NAMESPACE]: kibanaNamespace, }, [FormMonitorType.SINGLE]: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.SINGLE, [ConfigKey.NAMESPACE]: kibanaNamespace, }, [FormMonitorType.HTTP]: { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], isTLSEnabled: false, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, [ConfigKey.NAMESPACE]: kibanaNamespace, }, [FormMonitorType.TCP]: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], isTLSEnabled: false, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, [ConfigKey.NAMESPACE]: kibanaNamespace, }, [FormMonitorType.ICMP]: { - ...DEFAULT_FIELDS[DataStream.ICMP], + ...DEFAULT_FIELDS[MonitorTypeEnum.ICMP], [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.ICMP, [ConfigKey.NAMESPACE]: kibanaNamespace, }, @@ -68,9 +68,9 @@ export const formatDefaultFormValues = (monitor?: SyntheticsMonitor) => { // handle default monitor types from Uptime, which don't contain `ConfigKey.FORM_MONITOR_TYPE` if (!formMonitorType) { formMonitorType = - monitorType === DataStream.BROWSER + monitorType === MonitorTypeEnum.BROWSER ? FormMonitorType.MULTISTEP - : (monitorType as Omit as FormMonitorType); + : (monitorType as Omit as FormMonitorType); monitorWithFormMonitorType[ConfigKey.FORM_MONITOR_TYPE] = formMonitorType; } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index ddc1c27d6e7e6..b80c4ccb773b4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -64,7 +64,7 @@ import { getDocLinks } from '../../../../../kibana_services'; import { useMonitorName } from '../../../hooks/use_monitor_name'; import { ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, HTTPMethod, ScreenshotOption, @@ -869,7 +869,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ validation: () => ({ validate: { validResponseStatusCheck: (value) => { - const validateFn = validate[DataStream.HTTP][ConfigKey.RESPONSE_STATUS_CHECK]; + const validateFn = validate[MonitorTypeEnum.HTTP][ConfigKey.RESPONSE_STATUS_CHECK]; if (validateFn) { return !validateFn({ [ConfigKey.RESPONSE_STATUS_CHECK]: value, @@ -1049,7 +1049,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ validation: () => ({ validate: { validParams: (value) => { - const validateFn = validate[DataStream.BROWSER][ConfigKey.PARAMS]; + const validateFn = validate[MonitorTypeEnum.BROWSER][ConfigKey.PARAMS]; if (validateFn) { return validateFn({ [ConfigKey.PARAMS]: value, @@ -1335,7 +1335,7 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ validation: () => ({ validate: { validPlaywrightOptions: (value) => { - const validateFn = validate[DataStream.BROWSER][ConfigKey.PLAYWRIGHT_OPTIONS]; + const validateFn = validate[MonitorTypeEnum.BROWSER][ConfigKey.PLAYWRIGHT_OPTIONS]; if (validateFn) { return validateFn({ [ConfigKey.PLAYWRIGHT_OPTIONS]: value, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx index 3b6a815771c73..56f6093f5a43b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx @@ -6,7 +6,7 @@ */ import { format, ALLOWED_FIELDS } from './formatter'; -import { DataStream } from '../../../../../../common/runtime_types'; +import { MonitorTypeEnum } from '../../../../../../common/runtime_types'; import { DEFAULT_FIELDS, PROFILE_VALUES_ENUM, @@ -112,7 +112,7 @@ describe('format', () => { it.each([[true], [false]])('correctly formats form fields to monitor type', (enabled) => { formValues.enabled = enabled; expect(format(formValues)).toEqual({ - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: false, }, @@ -245,7 +245,7 @@ describe('format', () => { }, }; expect(format(browserFormFields)).toEqual({ - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], __ui: { script_source: { file_name: fileName, @@ -314,7 +314,7 @@ describe('format', () => { }, }) ).toEqual({ - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: isTLSEnabled, }, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.ts index 62f0919cb6875..d6ec887f324cb 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.ts @@ -5,11 +5,11 @@ * 2.0. */ import { get, pick } from 'lodash'; -import { ConfigKey, DataStream, FormMonitorType, MonitorFields } from '../types'; +import { ConfigKey, MonitorTypeEnum, FormMonitorType, MonitorFields } from '../types'; import { DEFAULT_FIELDS } from '../constants'; export const serializeNestedFormField = (fields: Record) => { - const monitorType = fields[ConfigKey.MONITOR_TYPE] as DataStream; + const monitorType = fields[ConfigKey.MONITOR_TYPE] as MonitorTypeEnum; const monitorFields: Record = {}; const defaults = DEFAULT_FIELDS[monitorType] as MonitorFields; Object.keys(defaults).map((key) => { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts index 4b4e524c9f3b0..0cbbfa5d91313 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts @@ -7,7 +7,7 @@ import { ConfigKey, - DataStream, + MonitorTypeEnum, HTTPFields, BrowserFields, MonitorFields, @@ -32,7 +32,7 @@ describe('[Monitor Management] validation', () => { }; it('should return false for all valid props', () => { - const validators = validate[DataStream.HTTP]; + const validators = validate[MonitorTypeEnum.HTTP]; const keysToValidate = [ ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, @@ -52,7 +52,7 @@ describe('[Monitor Management] validation', () => { describe.each([[ConfigKey.SOURCE_INLINE, 'step(() => {});']])('Browser', (configKey, value) => { const browserProps: Partial = { ...commonPropsValid, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.BROWSER, [ConfigKey.TIMEOUT]: null, [ConfigKey.URLS]: null, [ConfigKey.PORT]: null, @@ -60,7 +60,7 @@ describe('[Monitor Management] validation', () => { }; it('should return false for all valid props', () => { - const validators = validate[DataStream.BROWSER]; + const validators = validate[MonitorTypeEnum.BROWSER]; const keysToValidate = [ConfigKey.SCHEDULE, ConfigKey.TIMEOUT, configKey]; const validatorFns = keysToValidate.map((key) => validators[key]); const result = validatorFns.map((fn) => fn?.(browserProps as Partial) ?? true); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx index 332ee8e6fbc55..c22a89f63fa14 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx @@ -6,7 +6,7 @@ */ import { ConfigKey, - DataStream, + MonitorTypeEnum, ScheduleUnit, MonitorFields, Validator, @@ -87,7 +87,7 @@ const validateCommon: ValidationLibrary = { const { number, unit } = schedule as MonitorFields[ConfigKey.SCHEDULE]; // Timeout is not currently supported by browser monitors - if (monitorType === DataStream.BROWSER) { + if (monitorType === MonitorTypeEnum.BROWSER) { return false; } @@ -163,11 +163,11 @@ const validateBrowser: ValidationLibrary = { params ? !validJSONFormat(params) : false, }; -export type ValidateDictionary = Record; +export type ValidateDictionary = Record; export const validate: ValidateDictionary = { - [DataStream.HTTP]: validateHTTP, - [DataStream.TCP]: validateTCP, - [DataStream.ICMP]: validateICMP, - [DataStream.BROWSER]: validateBrowser, + [MonitorTypeEnum.HTTP]: validateHTTP, + [MonitorTypeEnum.TCP]: validateTCP, + [MonitorTypeEnum.ICMP]: validateICMP, + [MonitorTypeEnum.BROWSER]: validateBrowser, }; 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 7b78a45ccea23..5f8d264585f6f 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 @@ -28,7 +28,7 @@ import { useSelectedLocation } from '../hooks/use_selected_location'; import { getErrorDetailsUrl } from '../monitor_errors/errors_list'; import { ConfigKey, - DataStream, + MonitorTypeEnum, EncryptedSyntheticsSavedMonitor, Ping, SyntheticsJourneyApiResponse, @@ -122,7 +122,7 @@ export const LastTestRunComponent = ({ - {monitor?.type === DataStream.BROWSER ? ( + {monitor?.type === MonitorTypeEnum.BROWSER ? ( 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 de83a03ac4928..c61242fa2373b 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 @@ -38,7 +38,7 @@ import { getTestRunDetailRelativeLink, TestDetailsLink, } from '../../common/links/test_details_link'; -import { ConfigKey, DataStream, Ping } from '../../../../../../common/runtime_types'; +import { ConfigKey, MonitorTypeEnum, Ping } from '../../../../../../common/runtime_types'; import { formatTestDuration } from '../../../utils/monitor_test_result/test_time_formats'; import { sortPings } from '../../../utils/monitor_test_result/sort_pings'; import { selectPingsError } from '../../../state'; @@ -93,7 +93,7 @@ export const TestRunsTable = ({ const selectedLocation = useSelectedLocation(); const isTabletOrGreater = useIsWithinMinBreakpoint('s'); - const isBrowserMonitor = monitor?.[ConfigKey.MONITOR_TYPE] === DataStream.BROWSER; + const isBrowserMonitor = monitor?.[ConfigKey.MONITOR_TYPE] === MonitorTypeEnum.BROWSER; const { expandedRows, setExpandedRows } = useExpandedPingList(pings); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/filters/filter_fields.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/filters/filter_fields.ts index 778b39e2c37c7..40ef046a6579b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/filters/filter_fields.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/filters/filter_fields.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { invert } from 'lodash'; -import { DataStream, ServiceLocations } from '../../../../../common/runtime_types'; +import { MonitorTypeEnum, ServiceLocations } from '../../../../../common/runtime_types'; import { MonitorFilterState } from '../../state'; export type SyntheticsMonitorFilterField = keyof Omit< @@ -43,7 +43,7 @@ export function getSyntheticsFilterDisplayValues( switch (field) { case 'monitorTypes': return values.map(({ label, count }: { label: string; count: number }) => ({ - label: monitorTypeKeyLabelMap[label as DataStream] ?? label, + label: monitorTypeKeyLabelMap[label as MonitorTypeEnum] ?? label, count, })); case 'schedules': @@ -88,9 +88,9 @@ export const valueToLabelWithEmptyCount = (value?: string | string[]): LabelWith return value ? [{ label: value, count: 0 }] : []; }; -export const monitorTypeKeyLabelMap: Record = { - [DataStream.BROWSER]: 'Journey / Page', - [DataStream.HTTP]: 'HTTP', - [DataStream.TCP]: 'TCP', - [DataStream.ICMP]: 'ICMP', +export const monitorTypeKeyLabelMap: Record = { + [MonitorTypeEnum.BROWSER]: 'Journey / Page', + [MonitorTypeEnum.HTTP]: 'HTTP', + [MonitorTypeEnum.TCP]: 'TCP', + [MonitorTypeEnum.ICMP]: 'ICMP', }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index a57dcf8d75848..abfc08919b33a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -8,7 +8,7 @@ import { SyntheticsAppState } from '../../../state/root_reducer'; import { ConfigKey, - DataStream, + MonitorTypeEnum, DEFAULT_THROTTLING, LocationStatus, ScheduleUnit, @@ -227,7 +227,7 @@ function getMonitorDetailsMockSlice() { check_group: '051aba1c-0b74-11ed-9f0e-ba4e6fa109d5', id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', timespan: { lt: '2022-07-24T17:24:06.094Z', gte: '2022-07-24T17:14:06.094Z' }, - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, status: 'up', }, url: { @@ -293,7 +293,7 @@ function getMonitorDetailsMockSlice() { check_group: '051aba1c-0b74-11ed-9f0e-ba4e6fa109d5', id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', timespan: { lt: '2022-07-24T17:24:06.094Z', gte: '2022-07-24T17:14:06.094Z' }, - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, status: 'up', }, url: { @@ -348,7 +348,7 @@ function getMonitorDetailsMockSlice() { id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', check_group: '9eb87e53-0b72-11ed-b34f-aa618b4334ae', timespan: { lt: '2022-07-24T17:14:05.020Z', gte: '2022-07-24T17:04:05.020Z' }, - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, status: 'up', }, url: { @@ -403,7 +403,7 @@ function getMonitorDetailsMockSlice() { timespan: { lt: '2022-07-24T17:11:49.702Z', gte: '2022-07-24T17:01:49.702Z' }, check_group: '4e00ac5a-0b72-11ed-a97e-5203642c687d', id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, status: 'up', }, url: { @@ -436,7 +436,7 @@ function getMonitorDetailsMockSlice() { syntheticsMonitor: { id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', config_id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, enabled: true, schedule: { unit: ScheduleUnit.MINUTES, number: '10' }, 'service.name': '', diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts index 7d75a9644004d..c882f88e31158 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts @@ -12,7 +12,7 @@ import { CodeEditorMode, CommonFields, ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, HTTPAdvancedFields, HTTPFields, @@ -53,7 +53,7 @@ describe('validateMonitor', () => { testSchedule = { number: '5', unit: ScheduleUnit.MINUTES }; testTags = ['tag1', 'tag2']; testCommonFields = { - [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.ICMP, [ConfigKey.NAME]: 'test-monitor-name', [ConfigKey.CONFIG_ID]: 'test-monitor-id', [ConfigKey.MONITOR_QUERY_ID]: '', @@ -90,7 +90,7 @@ describe('validateMonitor', () => { ...testCommonFields, [ConfigKey.HOSTS]: 'test-hosts', [ConfigKey.WAIT]: '', - [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.ICMP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.ICMP, }; @@ -122,7 +122,7 @@ describe('validateMonitor', () => { ...testTCPSimpleFields, ...testTCPAdvancedFields, ...testTLSFields, - [ConfigKey.MONITOR_TYPE]: DataStream.TCP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.TCP, }; testHTTPSimpleFields = { @@ -153,7 +153,7 @@ describe('validateMonitor', () => { ...testHTTPSimpleFields, ...testHTTPAdvancedFields, ...testTLSFields, - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, }; testBrowserSimpleFields = { @@ -190,7 +190,7 @@ describe('validateMonitor', () => { testBrowserFields = { ...testBrowserSimpleFields, ...testBrowserAdvancedFields, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.BROWSER, }; }); @@ -207,7 +207,7 @@ describe('validateMonitor', () => { expect(result).toMatchObject({ valid: false, reason: 'Monitor type is invalid', - details: expect.stringMatching(/(?=.*invalid)(?=.*DataStream)/i), + details: 'Invalid value "undefined" supplied to "MonitorType"', }); }); @@ -223,7 +223,7 @@ describe('validateMonitor', () => { expect(result).toMatchObject({ valid: false, reason: 'Monitor type is invalid', - details: expect.stringMatching(/(?=.*invalid)(?=.*non-HTTP)(?=.*DataStream)/i), + details: 'Invalid value "non-HTTP" supplied to "MonitorType"', }); }); @@ -318,7 +318,7 @@ describe('validateMonitor', () => { expect(result.details).toEqual(expect.stringContaining(ConfigKey.HOSTS)); expect(result).toMatchObject({ valid: false, - reason: `Monitor is not a valid monitor of type ${DataStream.ICMP}`, + reason: `Monitor is not a valid monitor of type ${MonitorTypeEnum.ICMP}`, payload: testMonitor, }); }); @@ -337,7 +337,7 @@ describe('validateMonitor', () => { expect(result.details).toEqual(expect.stringContaining(ConfigKey.NAME)); expect(result).toMatchObject({ valid: false, - reason: `Monitor is not a valid monitor of type ${DataStream.TCP}`, + reason: `Monitor is not a valid monitor of type ${MonitorTypeEnum.TCP}`, payload: testMonitor, }); }); @@ -356,7 +356,7 @@ describe('validateMonitor', () => { expect(result.details).toEqual(expect.stringContaining(ConfigKey.URLS)); expect(result).toMatchObject({ valid: false, - reason: `Monitor is not a valid monitor of type ${DataStream.HTTP}`, + reason: `Monitor is not a valid monitor of type ${MonitorTypeEnum.HTTP}`, payload: testMonitor, }); }); @@ -375,7 +375,7 @@ describe('validateMonitor', () => { expect(result.details).toEqual(expect.stringContaining(ConfigKey.SOURCE_INLINE)); expect(result).toMatchObject({ valid: false, - reason: `Monitor is not a valid monitor of type ${DataStream.BROWSER}`, + reason: `Monitor is not a valid monitor of type ${MonitorTypeEnum.BROWSER}`, payload: testMonitor, }); }); diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts index 8b983c0c8f93c..cc6c34bf10206 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts @@ -15,8 +15,8 @@ import { ProjectMonitorCodec, ProjectMonitor, ConfigKey, - DataStream, - DataStreamCodec, + MonitorTypeEnum, + MonitorTypeCodec, HTTPFieldsCodec, MonitorFields, TCPFieldsCodec, @@ -33,11 +33,11 @@ type MonitorCodecType = | typeof HTTPFieldsCodec | typeof BrowserFieldsCodec; -const monitorTypeToCodecMap: Record = { - [DataStream.ICMP]: ICMPFieldsCodec, - [DataStream.TCP]: TCPFieldsCodec, - [DataStream.HTTP]: HTTPFieldsCodec, - [DataStream.BROWSER]: BrowserFieldsCodec, +const monitorTypeToCodecMap: Record = { + [MonitorTypeEnum.ICMP]: ICMPFieldsCodec, + [MonitorTypeEnum.TCP]: TCPFieldsCodec, + [MonitorTypeEnum.HTTP]: HTTPFieldsCodec, + [MonitorTypeEnum.BROWSER]: BrowserFieldsCodec, }; export interface ValidationResult { @@ -55,7 +55,7 @@ export interface ValidationResult { export function validateMonitor(monitorFields: MonitorFields): ValidationResult { const { [ConfigKey.MONITOR_TYPE]: monitorType } = monitorFields; - const decodedType = DataStreamCodec.decode(monitorType); + const decodedType = MonitorTypeCodec.decode(monitorType); if (isLeft(decodedType)) { return { diff --git a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts index 097367de05cad..299ab62f09028 100644 --- a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts +++ b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts @@ -11,7 +11,7 @@ import { SavedObject } from '@kbn/core/server'; import { SyntheticsMonitor, ConfigKey, - DataStream, + MonitorTypeEnum, ScheduleUnit, SourceType, } from '../../../common/runtime_types/monitor_management'; @@ -44,8 +44,8 @@ const testConfig: SavedObject = { updated_at: '2011-10-05T14:48:00.000Z', id, attributes: { - ...DEFAULT_FIELDS[DataStream.BROWSER], - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, [ConfigKey.LOCATIONS]: [ { id: 'us_central', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.test.ts index 666b0af1979c9..748611c9e2f54 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ConfigKey, DataStream } from '../../../../common/runtime_types'; +import { ConfigKey, MonitorTypeEnum } from '../../../../common/runtime_types'; import { formatSyntheticsPolicy } from './format_synthetics_policy'; import { PROFILE_VALUES_ENUM, PROFILES_MAP } from '../../../../common/constants/monitor_defaults'; @@ -13,7 +13,7 @@ describe('formatSyntheticsPolicy', () => { it('formats browser policy', () => { const { formattedPolicy } = formatSyntheticsPolicy( testNewPolicy, - DataStream.BROWSER, + MonitorTypeEnum.BROWSER, browserConfig, gParams ); @@ -468,7 +468,7 @@ describe('formatSyntheticsPolicy', () => { it.each([true, false])('formats http policy', (isTLSEnabled) => { const { formattedPolicy } = formatSyntheticsPolicy( testNewPolicy, - DataStream.HTTP, + MonitorTypeEnum.HTTP, { ...httpPolicy, [ConfigKey.METADATA]: { is_tls_enabled: isTLSEnabled }, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.ts index f371747342281..78cbc2d4a0790 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/private_formatters/format_synthetics_policy.ts @@ -9,7 +9,7 @@ import { NewPackagePolicy } from '@kbn/fleet-plugin/common'; import { cloneDeep } from 'lodash'; import { processorsFormatter } from './processors_formatter'; import { LegacyConfigKey } from '../../../../common/constants/monitor_management'; -import { ConfigKey, DataStream, MonitorFields } from '../../../../common/runtime_types'; +import { ConfigKey, MonitorTypeEnum, MonitorFields } from '../../../../common/runtime_types'; import { throttlingFormatter } from './browser_formatters'; import { replaceStringWithParams } from '../formatting_utils'; import { syntheticsPolicyFormatters } from './formatters'; @@ -28,7 +28,7 @@ export interface ProcessorFields { export const formatSyntheticsPolicy = ( newPolicy: NewPackagePolicy, - monitorType: DataStream, + monitorType: MonitorTypeEnum, config: Partial, params: Record, isLegacy?: boolean diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/convert_to_data_stream.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/convert_to_data_stream.ts index 78842274d02e0..69ba8ddb6deff 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/convert_to_data_stream.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/convert_to_data_stream.ts @@ -6,10 +6,10 @@ */ import { DEFAULT_NAMESPACE_STRING } from '../../../../common/constants/monitor_defaults'; -import { DataStream, MonitorFields } from '../../../../common/runtime_types'; +import { MonitorTypeEnum, MonitorFields } from '../../../../common/runtime_types'; export interface DataStreamConfig { - type: DataStream; + type: MonitorTypeEnum; id: string; schedule: string; enabled: boolean; @@ -19,7 +19,7 @@ export interface DataStreamConfig { streams: [ { data_stream: { - dataset: DataStream; + dataset: MonitorTypeEnum; type: 'synthetics'; }; } & Partial diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/format_configs.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/format_configs.test.ts index 78756c04174ba..979db50dee6d4 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/format_configs.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/public_formatters/format_configs.test.ts @@ -15,7 +15,7 @@ import { import { loggerMock } from '@kbn/logging-mocks'; import { ConfigKey, - DataStream, + MonitorTypeEnum, CodeEditorMode, MonitorFields, ResponseBodyIndexPolicy, @@ -25,7 +25,7 @@ import { } from '../../../../common/runtime_types'; const testHTTPConfig: Partial = { - type: 'http' as DataStream, + type: 'http' as MonitorTypeEnum, enabled: true, schedule: { number: '3', unit: 'm' as ScheduleUnit }, 'service.name': '', @@ -62,7 +62,7 @@ const testHTTPConfig: Partial = { }; const testBrowserConfig: Partial = { - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, enabled: true, schedule: { number: '3', unit: ScheduleUnit.MINUTES }, 'service.name': 'APM Service', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts index 44e70d3a2a95e..837006f5f6c3b 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { loggerMock } from '@kbn/logging-mocks'; import { - DataStream, + MonitorTypeEnum, MonitorFields, ScheduleUnit, SourceType, @@ -160,7 +160,7 @@ describe('SyntheticsPrivateLocation', () => { it('formats monitors stream properly', () => { const test = formatSyntheticsPolicy( testMonitorPolicy, - DataStream.BROWSER, + MonitorTypeEnum.BROWSER, dummyBrowserConfig, {} ); @@ -260,7 +260,7 @@ const dummyBrowserConfig: Partial & { fields: Record; fields_under_root: boolean; } = { - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, enabled: true, schedule: { unit: ScheduleUnit.MINUTES, number: '10' }, 'service.name': 'test service', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts index 2a4a2ec5fd1d7..ea60d8408addd 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.test.ts @@ -6,7 +6,7 @@ */ import { - DataStream, + MonitorTypeEnum, ScreenshotOption, Locations, LocationStatus, @@ -89,7 +89,7 @@ describe('browser normalizers', () => { locations: ['us_central', 'us_east'], tags: ['tag3', 'tag4'], ignoreHTTPSErrors: false, - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, hash: testHash, }, { @@ -105,7 +105,7 @@ describe('browser normalizers', () => { privateLocations: ['Germany'], tags: ['tag3', 'tag4'], ignoreHTTPSErrors: false, - type: DataStream.BROWSER, + type: MonitorTypeEnum.BROWSER, hash: testHash, }, ]; @@ -122,7 +122,7 @@ describe('browser normalizers', () => { expect(actual).toEqual([ { normalizedFields: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], journey_id: 'test-id-1', ignore_https_errors: true, origin: 'project', @@ -170,7 +170,7 @@ describe('browser normalizers', () => { }, { normalizedFields: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], journey_id: 'test-id-2', ignore_https_errors: false, origin: 'project', @@ -228,7 +228,7 @@ describe('browser normalizers', () => { }, { normalizedFields: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], journey_id: 'test-id-3', ignore_https_errors: false, origin: 'project', @@ -305,7 +305,7 @@ describe('browser normalizers', () => { expect(actual).toEqual([ { normalizedFields: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], journey_id: 'test-id-1', ignore_https_errors: true, origin: 'project', @@ -375,7 +375,7 @@ describe('browser normalizers', () => { expect(actual).toEqual([ { normalizedFields: { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], journey_id: 'test-id-1', ignore_https_errors: true, origin: 'project', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts index 0e3196d46d6f3..203dacc0e73ed 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/browser_monitor.ts @@ -8,7 +8,7 @@ import { BrowserFields, ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, ProjectMonitor, ThrottlingConfig, @@ -35,7 +35,7 @@ export const getNormalizeBrowserFields = ({ namespace, version, }: NormalizedProjectProps): NormalizerResult => { - const defaultFields = DEFAULT_FIELDS[DataStream.BROWSER]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.BROWSER]; const { errors, normalizedFields: commonFields } = getNormalizeCommonFields({ locations, @@ -50,7 +50,7 @@ export const getNormalizeBrowserFields = ({ const normalizedFields = { ...commonFields, - [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.BROWSER, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, [ConfigKey.SOURCE_PROJECT_CONTENT]: monitor.content || defaultFields[ConfigKey.SOURCE_PROJECT_CONTENT], @@ -80,7 +80,7 @@ export const getNormalizeBrowserFields = ({ export const normalizeThrottling = ( monitorThrottling: ProjectMonitor['throttling'] ): ThrottlingConfig => { - const defaultFields = DEFAULT_FIELDS[DataStream.BROWSER]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.BROWSER]; let throttling = defaultFields[ConfigKey.THROTTLING_CONFIG]; if (typeof monitorThrottling === 'boolean' && !monitorThrottling) { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts index ff0b03ad76015..e7aeb6a41577c 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts @@ -14,7 +14,7 @@ import { BrowserFields, ConfigKey, CommonFields, - DataStream, + MonitorTypeEnum, Locations, ProjectMonitor, ScheduleUnit, @@ -298,7 +298,7 @@ export const flattenAndFormatObject = (obj: Record, prefix = '' }, {}); export const normalizeYamlConfig = (monitor: NormalizedProjectProps['monitor']) => { - const defaultFields = DEFAULT_FIELDS[monitor.type as DataStream]; + const defaultFields = DEFAULT_FIELDS[monitor.type as MonitorTypeEnum]; const supportedKeys = Object.keys(defaultFields); const flattenedConfig = flattenAndFormatObject(monitor, '', supportedKeys); const { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts index 9fc0218835499..efd77695abebb 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts @@ -5,7 +5,7 @@ * 2.0. */ import { omit } from 'lodash'; -import { DataStream, Locations, LocationStatus } from '../../../../common/runtime_types'; +import { MonitorTypeEnum, Locations, LocationStatus } from '../../../../common/runtime_types'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { normalizeProjectMonitors } from '.'; import { PrivateLocationAttributes } from '../../../runtime_types/private_locations'; @@ -138,7 +138,7 @@ describe('http normalizers', () => { }, ], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: true, }, @@ -195,7 +195,7 @@ describe('http normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: true, }, @@ -278,7 +278,7 @@ describe('http normalizers', () => { }, ], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: true, }, @@ -335,7 +335,7 @@ describe('http normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], __ui: { is_tls_enabled: false, }, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts index c79db2433cb80..113b75924b1c1 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts @@ -9,7 +9,7 @@ import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { CodeEditorMode, ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, HTTPFields, TLSVersion, @@ -34,7 +34,7 @@ export const getNormalizeHTTPFields = ({ namespace, version, }: NormalizedProjectProps): NormalizerResult => { - const defaultFields = DEFAULT_FIELDS[DataStream.HTTP]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.HTTP]; const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); const { errors: commonErrors, normalizedFields: commonFields } = getNormalizeCommonFields({ @@ -61,7 +61,7 @@ export const getNormalizeHTTPFields = ({ const normalizedFields = { ...yamlConfig, ...commonFields, - [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, [ConfigKey.URLS]: getOptionalArrayField(monitor.urls) || defaultFields[ConfigKey.URLS], [ConfigKey.MAX_REDIRECTS]: formatMaxRedirects(monitor[ConfigKey.MAX_REDIRECTS]), @@ -78,7 +78,7 @@ export const getNormalizeHTTPFields = ({ ? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[]) : defaultFields[ConfigKey.TLS_VERSION], [ConfigKey.METADATA]: { - ...DEFAULT_FIELDS[DataStream.HTTP][ConfigKey.METADATA], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP][ConfigKey.METADATA], is_tls_enabled: getHasTLSFields(monitor), }, }; @@ -118,7 +118,7 @@ export const formatMaxRedirects = (value?: string | number): string => { return `${value}`; } - const defaultFields = DEFAULT_FIELDS[DataStream.HTTP]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.HTTP]; return value ?? defaultFields[ConfigKey.MAX_REDIRECTS]; }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts index 437025da4f789..acb9dc03abc7c 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DataStream, Locations, LocationStatus } from '../../../../common/runtime_types'; +import { MonitorTypeEnum, Locations, LocationStatus } from '../../../../common/runtime_types'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { normalizeProjectMonitors } from '.'; import { PrivateLocationAttributes } from '../../../runtime_types/private_locations'; @@ -99,7 +99,7 @@ describe('icmp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.ICMP], + ...DEFAULT_FIELDS[MonitorTypeEnum.ICMP], config_id: '', custom_heartbeat_id: 'Cloudflare-DNS-test-project-id-test-space', enabled: true, @@ -139,7 +139,7 @@ describe('icmp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.ICMP], + ...DEFAULT_FIELDS[MonitorTypeEnum.ICMP], config_id: '', custom_heartbeat_id: 'Cloudflare-DNS-2-test-project-id-test-space', enabled: true, @@ -192,7 +192,7 @@ describe('icmp normalizers', () => { }, ], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.ICMP], + ...DEFAULT_FIELDS[MonitorTypeEnum.ICMP], config_id: '', custom_heartbeat_id: 'Cloudflare-DNS-3-test-project-id-test-space', enabled: true, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts index b907c983d534b..8dcb062eb56fe 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/icmp_monitor.ts @@ -8,7 +8,7 @@ import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, ICMPFields, } from '../../../../common/runtime_types/monitor_management'; @@ -32,7 +32,7 @@ export const getNormalizeICMPFields = ({ namespace, version, }: NormalizedProjectProps): NormalizerResult => { - const defaultFields = DEFAULT_FIELDS[DataStream.ICMP]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.ICMP]; const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); @@ -61,7 +61,7 @@ export const getNormalizeICMPFields = ({ const normalizedFields = { ...yamlConfig, ...commonFields, - [ConfigKey.MONITOR_TYPE]: DataStream.ICMP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.ICMP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.ICMP, [ConfigKey.HOSTS]: getOptionalArrayField(monitor[ConfigKey.HOSTS]) || defaultFields[ConfigKey.HOSTS], diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts index 0bdb318731f31..e73d7da56483e 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/index.ts @@ -5,7 +5,7 @@ * 2.0. */ import { PrivateLocationAttributes } from '../../../runtime_types/private_locations'; -import { DataStream, Locations, ProjectMonitor } from '../../../../common/runtime_types'; +import { MonitorTypeEnum, Locations, ProjectMonitor } from '../../../../common/runtime_types'; import { getNormalizeBrowserFields } from './browser_monitor'; import { getNormalizeICMPFields } from './icmp_monitor'; import { getNormalizeTCPFields } from './tcp_monitor'; @@ -14,19 +14,19 @@ import { NormalizedProjectProps } from './common_fields'; export const normalizeProjectMonitor = (props: NormalizedProjectProps) => { const { monitor } = props; - const type = monitor.type || DataStream.BROWSER; + const type = monitor.type || MonitorTypeEnum.BROWSER; switch (type) { - case DataStream.BROWSER: + case MonitorTypeEnum.BROWSER: return getNormalizeBrowserFields(props); - case DataStream.HTTP: + case MonitorTypeEnum.HTTP: return getNormalizeHTTPFields(props); - case DataStream.TCP: + case MonitorTypeEnum.TCP: return getNormalizeTCPFields(props); - case DataStream.ICMP: + case MonitorTypeEnum.ICMP: return getNormalizeICMPFields(props); default: throw new Error(`Unsupported monitor type ${monitor.type}`); diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts index 26435e3841ecf..0b9e1488992dd 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.test.ts @@ -5,7 +5,7 @@ * 2.0. */ import { omit } from 'lodash'; -import { DataStream, Locations, LocationStatus } from '../../../../common/runtime_types'; +import { MonitorTypeEnum, Locations, LocationStatus } from '../../../../common/runtime_types'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { normalizeProjectMonitors } from '.'; import { PrivateLocationAttributes } from '../../../runtime_types/private_locations'; @@ -101,7 +101,7 @@ describe('tcp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: true, }, @@ -155,7 +155,7 @@ describe('tcp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: true, }, @@ -222,7 +222,7 @@ describe('tcp normalizers', () => { }, ], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: false, }, @@ -289,7 +289,7 @@ describe('tcp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: true, }, @@ -343,7 +343,7 @@ describe('tcp normalizers', () => { { errors: [], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: true, }, @@ -410,7 +410,7 @@ describe('tcp normalizers', () => { }, ], normalizedFields: { - ...DEFAULT_FIELDS[DataStream.TCP], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP], __ui: { is_tls_enabled: false, }, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts index 9626937d98a7b..5162d39005196 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/normalizers/tcp_monitor.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import { DEFAULT_FIELDS } from '../../../../common/constants/monitor_defaults'; import { ConfigKey, - DataStream, + MonitorTypeEnum, FormMonitorType, TCPFields, TLSVersion, @@ -33,7 +33,7 @@ export const getNormalizeTCPFields = ({ namespace, version, }: NormalizedProjectProps): NormalizerResult => { - const defaultFields = DEFAULT_FIELDS[DataStream.TCP]; + const defaultFields = DEFAULT_FIELDS[MonitorTypeEnum.TCP]; const errors = []; const { yamlConfig, unsupportedKeys } = normalizeYamlConfig(monitor); @@ -62,7 +62,7 @@ export const getNormalizeTCPFields = ({ const normalizedFields = { ...yamlConfig, ...commonFields, - [ConfigKey.MONITOR_TYPE]: DataStream.TCP, + [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.TCP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.TCP, [ConfigKey.HOSTS]: getOptionalArrayField(monitor[ConfigKey.HOSTS]) || defaultFields[ConfigKey.HOSTS], @@ -70,7 +70,7 @@ export const getNormalizeTCPFields = ({ ? (getOptionalListField(get(monitor, ConfigKey.TLS_VERSION)) as TLSVersion[]) : defaultFields[ConfigKey.TLS_VERSION], [ConfigKey.METADATA]: { - ...DEFAULT_FIELDS[DataStream.TCP][ConfigKey.METADATA], + ...DEFAULT_FIELDS[MonitorTypeEnum.TCP][ConfigKey.METADATA], is_tls_enabled: getHasTLSFields(monitor), }, }; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts index 79a8a658ef469..8684f31f9cd56 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts @@ -9,7 +9,7 @@ import { savedObjectsClientMock, savedObjectsServiceMock } from '@kbn/core/serve import { ProjectMonitorFormatter } from './project_monitor_formatter'; import { ConfigKey, - DataStream, + MonitorTypeEnum, Locations, LocationStatus, PrivateLocation, @@ -353,7 +353,7 @@ describe('ProjectMonitorFormatter', () => { const payloadData = [ { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], __ui: { script_source: { file_name: '', @@ -403,7 +403,7 @@ const payloadData = [ hash: 'lleklrkelkj', }, { - ...DEFAULT_FIELDS[DataStream.BROWSER], + ...DEFAULT_FIELDS[MonitorTypeEnum.BROWSER], __ui: { script_source: { file_name: '', diff --git a/x-pack/plugins/task_manager/server/config.test.ts b/x-pack/plugins/task_manager/server/config.test.ts index c196a334931ba..9d85c216546f8 100644 --- a/x-pack/plugins/task_manager/server/config.test.ts +++ b/x-pack/plugins/task_manager/server/config.test.ts @@ -13,6 +13,7 @@ describe('config validation', () => { expect(configSchema.validate(config)).toMatchInlineSnapshot(` Object { "allow_reading_invalid_state": true, + "claim_strategy": "default", "ephemeral_tasks": Object { "enabled": false, "request_capacity": 10, @@ -72,6 +73,7 @@ describe('config validation', () => { expect(configSchema.validate(config)).toMatchInlineSnapshot(` Object { "allow_reading_invalid_state": true, + "claim_strategy": "default", "ephemeral_tasks": Object { "enabled": false, "request_capacity": 10, @@ -129,6 +131,7 @@ describe('config validation', () => { expect(configSchema.validate(config)).toMatchInlineSnapshot(` Object { "allow_reading_invalid_state": true, + "claim_strategy": "default", "ephemeral_tasks": Object { "enabled": false, "request_capacity": 10, @@ -244,4 +247,13 @@ describe('config validation', () => { configSchema.validate(config); }).not.toThrowError(); }); + + test('the claim strategy is validated', () => { + const config = { claim_strategy: 'invalid-strategy' }; + expect(() => { + configSchema.validate(config); + }).toThrowErrorMatchingInlineSnapshot( + `"The claim strategy is invalid: Unknown task claiming strategy (invalid-strategy)"` + ); + }); }); diff --git a/x-pack/plugins/task_manager/server/config.ts b/x-pack/plugins/task_manager/server/config.ts index 490d25a7bdfb0..3be8b341c939e 100644 --- a/x-pack/plugins/task_manager/server/config.ts +++ b/x-pack/plugins/task_manager/server/config.ts @@ -6,6 +6,7 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import { getTaskClaimer } from './task_claimers'; export const MAX_WORKERS_LIMIT = 100; export const DEFAULT_MAX_WORKERS = 10; @@ -25,6 +26,8 @@ export const DEFAULT_METRICS_RESET_INTERVAL = 30 * 1000; // 30 seconds // At the default poll interval of 3sec, this averages over the last 15sec. export const DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW = 5; +export const CLAIM_STRATEGY_DEFAULT = 'default'; + export const taskExecutionFailureThresholdSchema = schema.object( { error_threshold: schema.number({ @@ -152,6 +155,7 @@ export const configSchema = schema.object( max: 100, min: 1, }), + claim_strategy: schema.string({ defaultValue: CLAIM_STRATEGY_DEFAULT }), }, { validate: (config) => { @@ -162,6 +166,11 @@ export const configSchema = schema.object( ) { return `The specified monitored_stats_required_freshness (${config.monitored_stats_required_freshness}) is invalid, as it is below the poll_interval (${config.poll_interval})`; } + try { + getTaskClaimer(config.claim_strategy); + } catch (err) { + return `The claim strategy is invalid: ${err.message}`; + } }, } ); 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 39a9afb0f0d14..6ae1d00a62243 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 @@ -85,6 +85,7 @@ describe('EphemeralTaskLifecycle', () => { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', ...config, }, elasticsearchAndSOAvailability$, diff --git a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts index f034feb154462..2fd4ceb74ca74 100644 --- a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts +++ b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts @@ -80,6 +80,7 @@ describe('managed configuration', () => { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }); logger = context.logger.get('taskManager'); diff --git a/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts b/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts index 6c9a9efeb558e..228d94ed87daf 100644 --- a/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts +++ b/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts @@ -57,6 +57,7 @@ const config = { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }; const getStatsWithTimestamp = ({ diff --git a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts index e457797d5ae1b..5becffa4e302c 100644 --- a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts @@ -73,6 +73,7 @@ const config: TaskManagerConfig = { }, version_conflict_threshold: 80, worker_utilization_running_average_window: 5, + claim_strategy: 'default', }; describe('createAggregator', () => { diff --git a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts index 689c9c882bee3..543707bf940b2 100644 --- a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts @@ -53,6 +53,7 @@ describe('Configuration Statistics Aggregator', () => { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }; const managedConfig = { diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts index daf3f2baf085d..7dc3460d98388 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts @@ -58,6 +58,7 @@ describe('createMonitoringStatsStream', () => { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }; it('returns the initial config used to configure Task Manager', async () => { diff --git a/x-pack/plugins/task_manager/server/plugin.test.ts b/x-pack/plugins/task_manager/server/plugin.test.ts index 1e7215d6d7a1b..5e178db3b99ad 100644 --- a/x-pack/plugins/task_manager/server/plugin.test.ts +++ b/x-pack/plugins/task_manager/server/plugin.test.ts @@ -78,6 +78,7 @@ const pluginInitializerContextParams = { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }; describe('TaskManagerPlugin', () => { 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 79b153f42a88d..24898d3c99385 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -83,6 +83,7 @@ describe('TaskPollingLifecycle', () => { max_attempts: 20, }, metrics_reset_interval: 3000, + claim_strategy: 'default', }, taskStore: mockTaskStore, logger: taskManagerLogger, diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index e7509df01cfc8..2024277ea94e0 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -41,7 +41,8 @@ import { identifyEsError, isEsCannotExecuteScriptError } from './lib/identify_es import { BufferedTaskStore } from './buffered_task_store'; import { TaskTypeDictionary } from './task_type_dictionary'; import { delayOnClaimConflicts } from './polling'; -import { TaskClaiming, ClaimOwnershipResult } from './queries/task_claiming'; +import { TaskClaiming } from './queries/task_claiming'; +import { ClaimOwnershipResult } from './task_claimers'; export interface ITaskEventEmitter { get events(): Observable; @@ -132,6 +133,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter ({ CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: [ @@ -82,6 +66,23 @@ describe('TaskClaiming', () => { .mockImplementation(() => mockApmTrans as any); }); + test(`should throw an error when invalid strategy specified`, () => { + const definitions = new TaskTypeDictionary(mockLogger()); + + expect(() => { + new TaskClaiming({ + logger: taskManagerLogger, + strategy: 'non-default', + definitions, + excludedTaskTypes: [], + unusedTypes: [], + taskStore: taskStoreMock.create({ taskManagerId: '' }), + maxAttempts: 2, + getCapacity: () => 10, + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unknown task claiming strategy (non-default)"`); + }); + test(`should log when a certain task type is skipped due to having a zero concurency configuration`, () => { const definitions = new TaskTypeDictionary(mockLogger()); definitions.registerTaskDefinitions({ @@ -117,6 +118,7 @@ describe('TaskClaiming', () => { new TaskClaiming({ logger: taskManagerLogger, + strategy: 'default', definitions, excludedTaskTypes: [], unusedTypes: [], @@ -130,1220 +132,4 @@ describe('TaskClaiming', () => { `"Task Manager will never claim tasks of the following types as their \\"maxConcurrency\\" is set to 0: limitedToZero, anotherLimitedToZero"` ); }); - - describe('claimAvailableTasks', () => { - function initialiseTestClaiming({ - storeOpts = {}, - taskClaimingOpts = {}, - hits = [generateFakeTasks(1)], - versionConflicts = 2, - excludedTaskTypes = [], - unusedTaskTypes = [], - }: { - storeOpts: Partial; - taskClaimingOpts: Partial; - hits?: ConcreteTaskInstance[][]; - versionConflicts?: number; - excludedTaskTypes?: string[]; - unusedTaskTypes?: string[]; - }) { - const definitions = storeOpts.definitions ?? taskDefinitions; - const store = taskStoreMock.create({ taskManagerId: storeOpts.taskManagerId }); - store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); - - if (hits.length === 1) { - store.fetch.mockResolvedValue({ docs: hits[0] }); - store.updateByQuery.mockResolvedValue({ - updated: hits[0].length, - version_conflicts: versionConflicts, - total: hits[0].length, - }); - } else { - for (const docs of hits) { - store.fetch.mockResolvedValueOnce({ docs }); - store.updateByQuery.mockResolvedValueOnce({ - updated: docs.length, - version_conflicts: versionConflicts, - total: docs.length, - }); - } - } - - const taskClaiming = new TaskClaiming({ - logger: taskManagerLogger, - definitions, - taskStore: store, - excludedTaskTypes, - unusedTypes: unusedTaskTypes, - maxAttempts: taskClaimingOpts.maxAttempts ?? 2, - getCapacity: taskClaimingOpts.getCapacity ?? (() => 10), - ...taskClaimingOpts, - }); - - return { taskClaiming, store }; - } - - async function testClaimAvailableTasks({ - storeOpts = {}, - taskClaimingOpts = {}, - claimingOpts, - hits = [generateFakeTasks(1)], - versionConflicts = 2, - excludedTaskTypes = [], - unusedTaskTypes = [], - }: { - storeOpts: Partial; - taskClaimingOpts: Partial; - claimingOpts: Omit; - hits?: ConcreteTaskInstance[][]; - versionConflicts?: number; - excludedTaskTypes?: string[]; - unusedTaskTypes?: string[]; - }) { - const getCapacity = taskClaimingOpts.getCapacity ?? (() => 10); - const { taskClaiming, store } = initialiseTestClaiming({ - storeOpts, - taskClaimingOpts, - excludedTaskTypes, - unusedTaskTypes, - hits, - versionConflicts, - }); - - const results = await getAllAsPromise(taskClaiming.claimAvailableTasks(claimingOpts)); - - expect(apm.startTransaction).toHaveBeenCalledWith( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - expect(mockApmTrans.end).toHaveBeenCalledWith('success'); - - expect(store.updateByQuery.mock.calls[0][1]).toMatchObject({ - max_docs: getCapacity(), - }); - expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: getCapacity() }); - return results.map((result, index) => ({ - result, - args: { - search: store.fetch.mock.calls[index][0] as SearchOpts & { - query: MustNotCondition; - }, - updateByQuery: store.updateByQuery.mock.calls[index] as [ - UpdateByQuerySearchOpts, - UpdateByQueryOpts - ], - }, - })); - } - - test('makes calls to APM as expected when markAvailableTasksAsClaimed throws error', async () => { - const maxAttempts = _.random(2, 43); - const customMaxAttempts = _.random(44, 100); - - const definitions = new TaskTypeDictionary(mockLogger()); - definitions.registerTaskDefinitions({ - foo: { - title: 'foo', - createTaskRunner: jest.fn(), - }, - bar: { - title: 'bar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - }); - - const { taskClaiming, store } = initialiseTestClaiming({ - storeOpts: { - definitions, - }, - taskClaimingOpts: { - maxAttempts, - }, - }); - - store.updateByQuery.mockRejectedValue(new Error('Oh no')); - - await expect( - getAllAsPromise( - taskClaiming.claimAvailableTasks({ - claimOwnershipUntil: new Date(), - }) - ) - ).rejects.toMatchInlineSnapshot(`[Error: Oh no]`); - - expect(apm.startTransaction).toHaveBeenCalledWith( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); - }); - - test('it filters claimed tasks down by supported types, maxAttempts, status, and runAt', async () => { - const maxAttempts = _.random(2, 43); - const customMaxAttempts = _.random(44, 100); - - const definitions = new TaskTypeDictionary(mockLogger()); - definitions.registerTaskDefinitions({ - foo: { - title: 'foo', - createTaskRunner: jest.fn(), - }, - bar: { - title: 'bar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - foobar: { - title: 'foobar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - }); - - const [ - { - args: { - updateByQuery: [{ query, sort }], - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - definitions, - }, - taskClaimingOpts: { - maxAttempts, - }, - claimingOpts: { - claimOwnershipUntil: new Date(), - }, - excludedTaskTypes: ['foobar'], - }); - expect(query).toMatchObject({ - bool: { - must: [ - { - bool: { - must: [ - { - term: { - 'task.enabled': true, - }, - }, - ], - }, - }, - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - }, - }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - ], - filter: [ - { - bool: { - must_not: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - must: { range: { 'task.retryAt': { gt: 'now' } } }, - }, - }, - ], - }, - }, - ], - }, - }); - expect(sort).toMatchObject([ - { - _script: { - type: 'number', - order: 'asc', - script: { - lang: 'painless', - source: ` -if (doc['task.retryAt'].size()!=0) { - return doc['task.retryAt'].value.toInstant().toEpochMilli(); -} -if (doc['task.runAt'].size()!=0) { - return doc['task.runAt'].value.toInstant().toEpochMilli(); -} - `, - }, - }, - }, - ]); - }); - - test('it should claim in batches partitioned by maxConcurrency', async () => { - const maxAttempts = _.random(2, 43); - const definitions = new TaskTypeDictionary(mockLogger()); - const taskManagerId = uuidv1(); - const fieldUpdates = { - ownerId: taskManagerId, - retryAt: new Date(Date.now()), - }; - definitions.registerTaskDefinitions({ - unlimited: { - title: 'unlimited', - createTaskRunner: jest.fn(), - }, - limitedToZero: { - title: 'limitedToZero', - maxConcurrency: 0, - createTaskRunner: jest.fn(), - }, - anotherUnlimited: { - title: 'anotherUnlimited', - createTaskRunner: jest.fn(), - }, - finalUnlimited: { - title: 'finalUnlimited', - createTaskRunner: jest.fn(), - }, - limitedToOne: { - title: 'limitedToOne', - maxConcurrency: 1, - createTaskRunner: jest.fn(), - }, - anotherLimitedToOne: { - title: 'anotherLimitedToOne', - maxConcurrency: 1, - createTaskRunner: jest.fn(), - }, - limitedToTwo: { - title: 'limitedToTwo', - maxConcurrency: 2, - createTaskRunner: jest.fn(), - }, - }); - const results = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - definitions, - }, - taskClaimingOpts: { - maxAttempts, - getCapacity: (type) => { - switch (type) { - case 'limitedToOne': - case 'anotherLimitedToOne': - return 1; - case 'limitedToTwo': - return 2; - default: - return 10; - } - }, - }, - claimingOpts: { - claimOwnershipUntil: new Date(), - }, - }); - - expect(results.length).toEqual(4); - - expect(results[0].args.updateByQuery[1].max_docs).toEqual(10); - expect(results[0].args.updateByQuery[0].script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['unlimited', 'anotherUnlimited', 'finalUnlimited'], - skippedTaskTypes: [ - 'limitedToZero', - 'limitedToOne', - 'anotherLimitedToOne', - 'limitedToTwo', - ], - unusedTaskTypes: [], - taskMaxAttempts: { - unlimited: maxAttempts, - }, - }, - }); - - expect(results[1].args.updateByQuery[1].max_docs).toEqual(1); - expect(results[1].args.updateByQuery[0].script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['limitedToOne'], - skippedTaskTypes: [ - 'unlimited', - 'limitedToZero', - 'anotherUnlimited', - 'finalUnlimited', - 'anotherLimitedToOne', - 'limitedToTwo', - ], - taskMaxAttempts: { - limitedToOne: maxAttempts, - }, - }, - }); - - expect(results[2].args.updateByQuery[1].max_docs).toEqual(1); - expect(results[2].args.updateByQuery[0].script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['anotherLimitedToOne'], - skippedTaskTypes: [ - 'unlimited', - 'limitedToZero', - 'anotherUnlimited', - 'finalUnlimited', - 'limitedToOne', - 'limitedToTwo', - ], - taskMaxAttempts: { - anotherLimitedToOne: maxAttempts, - }, - }, - }); - - expect(results[3].args.updateByQuery[1].max_docs).toEqual(2); - expect(results[3].args.updateByQuery[0].script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['limitedToTwo'], - skippedTaskTypes: [ - 'unlimited', - 'limitedToZero', - 'anotherUnlimited', - 'finalUnlimited', - 'limitedToOne', - 'anotherLimitedToOne', - ], - taskMaxAttempts: { - limitedToTwo: maxAttempts, - }, - }, - }); - }); - - test('it should reduce the available capacity from batch to batch', async () => { - const maxAttempts = _.random(2, 43); - const definitions = new TaskTypeDictionary(mockLogger()); - const taskManagerId = uuidv1(); - definitions.registerTaskDefinitions({ - unlimited: { - title: 'unlimited', - createTaskRunner: jest.fn(), - }, - limitedToFive: { - title: 'limitedToFive', - maxConcurrency: 5, - createTaskRunner: jest.fn(), - }, - limitedToTwo: { - title: 'limitedToTwo', - maxConcurrency: 2, - createTaskRunner: jest.fn(), - }, - }); - const results = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - definitions, - }, - taskClaimingOpts: { - maxAttempts, - getCapacity: (type) => { - switch (type) { - case 'limitedToTwo': - return 2; - case 'limitedToFive': - return 5; - default: - return 10; - } - }, - }, - hits: [ - [ - // 7 returned by unlimited query - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - mockInstance({ - taskType: 'unlimited', - }), - ], - // 2 returned by limitedToFive query - [ - mockInstance({ - taskType: 'limitedToFive', - }), - mockInstance({ - taskType: 'limitedToFive', - }), - ], - // 1 reterned by limitedToTwo query - [ - mockInstance({ - taskType: 'limitedToTwo', - }), - ], - ], - claimingOpts: { - claimOwnershipUntil: new Date(), - }, - }); - - expect(results.length).toEqual(3); - - expect(results[0].args.updateByQuery[1].max_docs).toEqual(10); - - // only capacity for 3, even though 5 are allowed - expect(results[1].args.updateByQuery[1].max_docs).toEqual(3); - - // only capacity for 1, even though 2 are allowed - expect(results[2].args.updateByQuery[1].max_docs).toEqual(1); - }); - - test('it shuffles the types claimed in batches to ensure no type starves another', async () => { - const maxAttempts = _.random(2, 43); - const definitions = new TaskTypeDictionary(mockLogger()); - const taskManagerId = uuidv1(); - definitions.registerTaskDefinitions({ - unlimited: { - title: 'unlimited', - createTaskRunner: jest.fn(), - }, - anotherUnlimited: { - title: 'anotherUnlimited', - createTaskRunner: jest.fn(), - }, - finalUnlimited: { - title: 'finalUnlimited', - createTaskRunner: jest.fn(), - }, - limitedToOne: { - title: 'limitedToOne', - maxConcurrency: 1, - createTaskRunner: jest.fn(), - }, - anotherLimitedToOne: { - title: 'anotherLimitedToOne', - maxConcurrency: 1, - createTaskRunner: jest.fn(), - }, - limitedToTwo: { - title: 'limitedToTwo', - maxConcurrency: 2, - createTaskRunner: jest.fn(), - }, - }); - - const { taskClaiming, store } = initialiseTestClaiming({ - storeOpts: { - taskManagerId, - definitions, - }, - taskClaimingOpts: { - maxAttempts, - getCapacity: (type) => { - switch (type) { - case 'limitedToOne': - case 'anotherLimitedToOne': - return 1; - case 'limitedToTwo': - return 2; - default: - return 10; - } - }, - }, - }); - - async function getUpdateByQueryScriptParams() { - return ( - await getAllAsPromise( - taskClaiming.claimAvailableTasks({ - claimOwnershipUntil: new Date(), - }) - ) - ).map( - (result, index) => - ( - store.updateByQuery.mock.calls[index][0] as { - query: MustNotCondition; - size: number; - sort: string | string[]; - script: { - params: { - [claimableTaskTypes: string]: string[]; - }; - }; - } - ).script.params.claimableTaskTypes - ); - } - - const firstCycle = await getUpdateByQueryScriptParams(); - store.updateByQuery.mockClear(); - const secondCycle = await getUpdateByQueryScriptParams(); - - expect(firstCycle.length).toEqual(4); - expect(secondCycle.length).toEqual(4); - expect(firstCycle).not.toMatchObject(secondCycle); - }); - - test('it passes any unusedTaskTypes to script', async () => { - const maxAttempts = _.random(2, 43); - const customMaxAttempts = _.random(44, 100); - const taskManagerId = uuidv1(); - const fieldUpdates = { - ownerId: taskManagerId, - retryAt: new Date(Date.now()), - }; - const definitions = new TaskTypeDictionary(mockLogger()); - definitions.registerTaskDefinitions({ - foo: { - title: 'foo', - createTaskRunner: jest.fn(), - }, - bar: { - title: 'bar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - foobar: { - title: 'foobar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - }); - - const [ - { - args: { - updateByQuery: [{ query, script }], - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - definitions, - taskManagerId, - }, - taskClaimingOpts: { - maxAttempts, - }, - claimingOpts: { - claimOwnershipUntil: new Date(), - }, - excludedTaskTypes: ['foobar'], - unusedTaskTypes: ['barfoo'], - }); - expect(query).toMatchObject({ - bool: { - must: [ - { - bool: { - must: [ - { - term: { - 'task.enabled': true, - }, - }, - ], - }, - }, - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - }, - }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - ], - filter: [ - { - bool: { - must_not: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - must: { range: { 'task.retryAt': { gt: 'now' } } }, - }, - }, - ], - }, - }, - ], - }, - }); - expect(script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['foo', 'bar'], - skippedTaskTypes: ['foobar'], - unusedTaskTypes: ['barfoo'], - taskMaxAttempts: { - bar: customMaxAttempts, - foo: maxAttempts, - }, - }, - }); - }); - - test('it claims tasks by setting their ownerId, status and retryAt', async () => { - const taskManagerId = uuidv1(); - const claimOwnershipUntil = new Date(Date.now()); - const fieldUpdates = { - ownerId: taskManagerId, - retryAt: claimOwnershipUntil, - }; - const [ - { - args: { - updateByQuery: [{ script }], - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - }, - taskClaimingOpts: {}, - claimingOpts: { - claimOwnershipUntil, - }, - }); - expect(script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['report', 'dernstraight', 'yawn'], - skippedTaskTypes: [], - taskMaxAttempts: { - dernstraight: 2, - report: 2, - yawn: 2, - }, - }, - }); - }); - - test('it filters out running tasks', async () => { - const taskManagerId = uuidv1(); - const claimOwnershipUntil = new Date(Date.now()); - const runAt = new Date(); - const tasks = [ - mockInstance({ - id: 'aaa', - runAt, - taskType: 'yawn', - schedule: undefined, - attempts: 0, - status: TaskStatus.Claiming, - params: { hello: 'world' }, - state: { baby: 'Henhen' }, - user: 'jimbo', - scope: ['reporting'], - ownerId: taskManagerId, - }), - ]; - const [ - { - result: { docs }, - args: { - search: { query }, - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - }, - taskClaimingOpts: {}, - claimingOpts: { - claimOwnershipUntil, - }, - hits: [tasks], - }); - - expect(query).toMatchObject({ - bool: { - must: [ - { - term: { - 'task.ownerId': taskManagerId, - }, - }, - { term: { 'task.status': 'claiming' } }, - { - bool: { - should: [ - { - term: { - 'task.taskType': 'report', - }, - }, - { - term: { - 'task.taskType': 'dernstraight', - }, - }, - { - term: { - 'task.taskType': 'yawn', - }, - }, - ], - }, - }, - ], - }, - }); - - expect(docs).toMatchObject([ - { - attempts: 0, - id: 'aaa', - schedule: undefined, - params: { hello: 'world' }, - runAt, - scope: ['reporting'], - state: { baby: 'Henhen' }, - status: 'claiming', - taskType: 'yawn', - user: 'jimbo', - ownerId: taskManagerId, - }, - ]); - }); - - test('it returns task objects', async () => { - const taskManagerId = uuidv1(); - const claimOwnershipUntil = new Date(Date.now()); - const runAt = new Date(); - const tasks = [ - mockInstance({ - id: 'aaa', - runAt, - taskType: 'yawn', - schedule: undefined, - attempts: 0, - status: TaskStatus.Claiming, - params: { hello: 'world' }, - state: { baby: 'Henhen' }, - user: 'jimbo', - scope: ['reporting'], - ownerId: taskManagerId, - }), - mockInstance({ - id: 'bbb', - runAt, - taskType: 'yawn', - schedule: { interval: '5m' }, - attempts: 2, - status: TaskStatus.Claiming, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - }), - ]; - const [ - { - result: { docs }, - args: { - search: { query }, - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - }, - taskClaimingOpts: {}, - claimingOpts: { - claimOwnershipUntil, - }, - hits: [tasks], - }); - - expect(query).toMatchObject({ - bool: { - must: [ - { - term: { - 'task.ownerId': taskManagerId, - }, - }, - { term: { 'task.status': 'claiming' } }, - { - bool: { - should: [ - { - term: { - 'task.taskType': 'report', - }, - }, - { - term: { - 'task.taskType': 'dernstraight', - }, - }, - { - term: { - 'task.taskType': 'yawn', - }, - }, - ], - }, - }, - ], - }, - }); - - expect(docs).toMatchObject([ - { - attempts: 0, - id: 'aaa', - schedule: undefined, - params: { hello: 'world' }, - runAt, - scope: ['reporting'], - state: { baby: 'Henhen' }, - status: 'claiming', - taskType: 'yawn', - user: 'jimbo', - ownerId: taskManagerId, - }, - { - attempts: 2, - id: 'bbb', - schedule: { interval: '5m' }, - params: { shazm: 1 }, - runAt, - scope: ['reporting', 'ceo'], - state: { henry: 'The 8th' }, - status: 'claiming', - taskType: 'yawn', - user: 'dabo', - ownerId: taskManagerId, - }, - ]); - }); - - test('it returns version_conflicts that do not include conflicts that were proceeded against', async () => { - const taskManagerId = uuidv1(); - const claimOwnershipUntil = new Date(Date.now()); - const runAt = new Date(); - const tasks = [ - mockInstance({ - runAt, - taskType: 'foo', - schedule: undefined, - attempts: 0, - status: TaskStatus.Claiming, - params: { hello: 'world' }, - state: { baby: 'Henhen' }, - user: 'jimbo', - scope: ['reporting'], - ownerId: taskManagerId, - }), - mockInstance({ - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: TaskStatus.Claiming, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - }), - ]; - const maxDocs = 10; - const [ - { - result: { - stats: { tasksUpdated, tasksConflicted, tasksClaimed }, - }, - }, - ] = await testClaimAvailableTasks({ - storeOpts: { - taskManagerId, - }, - taskClaimingOpts: { getCapacity: () => maxDocs }, - claimingOpts: { - claimOwnershipUntil, - }, - hits: [tasks], - // assume there were 20 version conflists, but thanks to `conflicts="proceed"` - // we proceeded to claim tasks - versionConflicts: 20, - }); - - expect(tasksUpdated).toEqual(2); - // ensure we only count conflicts that *may* have counted against max_docs, no more than that - expect(tasksConflicted).toEqual(10 - tasksUpdated!); - expect(tasksClaimed).toEqual(2); - }); - }); - - describe('task events', () => { - function generateTasks(taskManagerId: string) { - const runAt = new Date(); - const tasks = [ - { - id: 'claimed-by-id', - runAt, - taskType: 'foo', - schedule: undefined, - attempts: 0, - status: TaskStatus.Claiming, - params: { hello: 'world' }, - state: { baby: 'Henhen' }, - user: 'jimbo', - scope: ['reporting'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - traceparent: 'parent', - }, - { - id: 'claimed-by-schedule', - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: TaskStatus.Claiming, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - traceparent: 'newParent', - }, - { - id: 'already-running', - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: TaskStatus.Running, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - traceparent: '', - }, - ]; - - return { taskManagerId, runAt, tasks }; - } - - function instantiateStoreWithMockedApiResponses({ - taskManagerId = uuidv4(), - definitions = taskDefinitions, - getCapacity = () => 10, - tasksClaimed, - }: Partial> & { - taskManagerId?: string; - tasksClaimed?: ConcreteTaskInstance[][]; - } = {}) { - const { runAt, tasks: generatedTasks } = generateTasks(taskManagerId); - const taskCycles = tasksClaimed ?? [generatedTasks]; - - const taskStore = taskStoreMock.create({ taskManagerId }); - taskStore.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); - for (const docs of taskCycles) { - taskStore.fetch.mockResolvedValueOnce({ docs }); - taskStore.updateByQuery.mockResolvedValueOnce({ - updated: docs.length, - version_conflicts: 0, - total: docs.length, - }); - } - - taskStore.fetch.mockResolvedValue({ docs: [] }); - taskStore.updateByQuery.mockResolvedValue({ - updated: 0, - version_conflicts: 0, - total: 0, - }); - - const taskClaiming = new TaskClaiming({ - logger: taskManagerLogger, - definitions, - excludedTaskTypes: [], - unusedTypes: [], - taskStore, - maxAttempts: 2, - getCapacity, - }); - - return { taskManagerId, runAt, taskClaiming }; - } - - test('emits an event when a task is succesfully by scheduling', async () => { - const { taskManagerId, runAt, taskClaiming } = instantiateStoreWithMockedApiResponses(); - - const promise = taskClaiming.events - .pipe( - filter( - (event: TaskEvent) => event.id === 'claimed-by-schedule' - ), - take(1) - ) - .toPromise(); - - await getFirstAsPromise( - taskClaiming.claimAvailableTasks({ - claimOwnershipUntil: new Date(), - }) - ); - - const event = await promise; - expect(event).toMatchObject( - asTaskClaimEvent( - 'claimed-by-schedule', - asOk({ - id: 'claimed-by-schedule', - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: 'claiming' as TaskStatus, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - traceparent: 'newParent', - }) - ) - ); - }); - }); }); - -function generateFakeTasks(count: number = 1) { - return _.times(count, (index) => mockInstance({ id: `task:id-${index}` })); -} - -function mockInstance(instance: Partial = {}) { - return Object.assign( - { - id: uuidv4(), - taskType: 'bar', - sequenceNumber: 32, - primaryTerm: 32, - runAt: new Date(), - scheduledAt: new Date(), - startedAt: null, - retryAt: null, - attempts: 0, - params: {}, - scope: ['reporting'], - state: {}, - status: 'idle', - user: 'example', - ownerId: null, - traceparent: '', - }, - instance - ); -} - -function getFirstAsPromise(obs$: Observable): Promise { - return new Promise((resolve, reject) => { - obs$.subscribe(resolve, reject); - }); -} -function getAllAsPromise(obs$: Observable): Promise { - return new Promise((resolve, reject) => { - obs$.pipe(toArray()).subscribe(resolve, reject); - }); -} diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.ts index 60237338fb09c..1e31bb5a2ed65 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.ts @@ -8,41 +8,30 @@ /* * This module contains helpers for managing the task manager storage layer. */ -import apm from 'elastic-apm-node'; -import minimatch from 'minimatch'; -import { Subject, Observable, from, of } from 'rxjs'; -import { map, mergeScan } from 'rxjs/operators'; -import { groupBy, pick, isPlainObject } from 'lodash'; +import { Subject, Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { groupBy, isPlainObject } from 'lodash'; import { Logger } from '@kbn/core/server'; import { asOk, asErr, Result } from '../lib/result_type'; import { ConcreteTaskInstance } from '../task'; -import { TaskClaim, asTaskClaimEvent, startTaskTimer, TaskTiming } from '../task_events'; -import { shouldBeOneOf, mustBeAllOf, filterDownBy, matchesClauses } from './query_clauses'; +import { TaskClaim } from '../task_events'; -import { - updateFieldsAndMarkAsFailed, - IdleTaskWithExpiredRunAt, - InactiveTasks, - RunningOrClaimingTaskWithExpiredRetryAt, - SortByRunAtAndRetryAt, - tasksClaimedByOwner, - tasksOfType, - EnabledTask, -} from './mark_available_tasks_as_claimed'; import { TaskTypeDictionary } from '../task_type_dictionary'; -import { - correctVersionConflictsForContinuation, - TaskStore, - UpdateByQueryResult, - SearchOpts, -} from '../task_store'; +import { TaskStore, UpdateByQueryResult } from '../task_store'; import { FillPoolResult } from '../lib/fill_pool'; -import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; +import { + TaskClaimerOpts, + TaskClaimerFn, + ClaimOwnershipResult, + getTaskClaimer, +} from '../task_claimers'; +export type { ClaimOwnershipResult } from '../task_claimers'; export interface TaskClaimingOpts { logger: Logger; + strategy: string; definitions: TaskTypeDictionary; unusedTypes: string[]; taskStore: TaskStore; @@ -67,31 +56,25 @@ export interface FetchResult { docs: ConcreteTaskInstance[]; } -export interface ClaimOwnershipResult { - stats: { - tasksUpdated: number; - tasksConflicted: number; - tasksClaimed: number; - }; - docs: ConcreteTaskInstance[]; - timing?: TaskTiming; +export function isClaimOwnershipResult(result: unknown): result is ClaimOwnershipResult { + return ( + isPlainObject((result as ClaimOwnershipResult).stats) && + Array.isArray((result as ClaimOwnershipResult).docs) + ); } -export const isClaimOwnershipResult = (result: unknown): result is ClaimOwnershipResult => - isPlainObject((result as ClaimOwnershipResult).stats) && - Array.isArray((result as ClaimOwnershipResult).docs); -enum BatchConcurrency { +export enum BatchConcurrency { Unlimited, Limited, } -type TaskClaimingBatches = Array; -interface TaskClaimingBatch { +export type TaskClaimingBatches = Array; +export interface TaskClaimingBatch { concurrency: Concurrency; tasksTypes: TaskType; } -type UnlimitedBatch = TaskClaimingBatch>; -type LimitedBatch = TaskClaimingBatch; +export type UnlimitedBatch = TaskClaimingBatch>; +export type LimitedBatch = TaskClaimingBatch; export const TASK_MANAGER_MARK_AS_CLAIMED = 'mark-available-tasks-as-claimed'; @@ -108,6 +91,7 @@ export class TaskClaiming { private readonly taskMaxAttempts: Record; private readonly excludedTaskTypes: string[]; private readonly unusedTypes: string[]; + private readonly taskClaimer: TaskClaimerFn; /** * Constructs a new TaskStore. @@ -120,12 +104,12 @@ export class TaskClaiming { this.maxAttempts = opts.maxAttempts; this.taskStore = opts.taskStore; this.getCapacity = opts.getCapacity; - this.logger = opts.logger; + this.logger = opts.logger.get('taskClaiming'); this.taskClaimingBatchesByType = this.partitionIntoClaimingBatches(this.definitions); this.taskMaxAttempts = Object.fromEntries(this.normalizeMaxAttempts(this.definitions)); this.excludedTaskTypes = opts.excludedTaskTypes; this.unusedTypes = opts.unusedTypes; - + this.taskClaimer = getTaskClaimer(opts.strategy); this.events$ = new Subject(); } @@ -177,224 +161,43 @@ export class TaskClaiming { return this.events$; } - private emitEvents = (events: TaskClaim[]) => { - events.forEach((event) => this.events$.next(event)); - }; - public claimAvailableTasksIfCapacityIsAvailable( claimingOptions: Omit ): Observable> { if (this.getCapacity()) { - return this.claimAvailableTasks(claimingOptions).pipe( - map((claimResult) => asOk(claimResult)) - ); + const opts: TaskClaimerOpts = { + batches: this.getClaimingBatches(), + claimOwnershipUntil: claimingOptions.claimOwnershipUntil, + taskStore: this.taskStore, + events$: this.events$, + getCapacity: this.getCapacity, + unusedTypes: this.unusedTypes, + definitions: this.definitions, + taskMaxAttempts: this.taskMaxAttempts, + excludedTaskTypes: this.excludedTaskTypes, + }; + return this.taskClaimer(opts).pipe(map((claimResult) => asOk(claimResult))); } this.logger.debug( `[Task Ownership]: Task Manager has skipped Claiming Ownership of available tasks at it has ran out Available Workers.` ); return of(asErr(FillPoolResult.NoAvailableWorkers)); } - - public claimAvailableTasks({ - claimOwnershipUntil, - }: Omit): Observable { - const initialCapacity = this.getCapacity(); - return from(this.getClaimingBatches()).pipe( - mergeScan( - (accumulatedResult, batch) => { - const stopTaskTimer = startTaskTimer(); - const capacity = Math.min( - initialCapacity - accumulatedResult.stats.tasksClaimed, - isLimited(batch) ? this.getCapacity(batch.tasksTypes) : this.getCapacity() - ); - // if we have no more capacity, short circuit here - if (capacity <= 0) { - return of(accumulatedResult); - } - return from( - this.executeClaimAvailableTasks({ - claimOwnershipUntil, - size: capacity, - taskTypes: isLimited(batch) ? new Set([batch.tasksTypes]) : batch.tasksTypes, - }).then((result) => { - const { stats, docs } = accumulateClaimOwnershipResults(accumulatedResult, result); - stats.tasksConflicted = correctVersionConflictsForContinuation( - stats.tasksClaimed, - stats.tasksConflicted, - initialCapacity - ); - return { stats, docs, timing: stopTaskTimer() }; - }) - ); - }, - // initialise the accumulation with no results - accumulateClaimOwnershipResults(), - // only run one batch at a time - 1 - ) - ); - } - - private executeClaimAvailableTasks = async ({ - claimOwnershipUntil, - size, - taskTypes, - }: OwnershipClaimingOpts): Promise => { - const { updated: tasksUpdated, version_conflicts: tasksConflicted } = - await this.markAvailableTasksAsClaimed({ - claimOwnershipUntil, - size, - taskTypes, - }); - - const docs = tasksUpdated > 0 ? await this.sweepForClaimedTasks(taskTypes, size) : []; - - this.emitEvents(docs.map((doc) => asTaskClaimEvent(doc.id, asOk(doc)))); - - const stats = { - tasksUpdated, - tasksConflicted, - tasksClaimed: docs.length, - }; - - return { - stats, - docs, - }; - }; - - private isTaskTypeExcluded(taskType: string) { - for (const excludedType of this.excludedTaskTypes) { - if (minimatch(taskType, excludedType)) { - return true; - } - } - - return false; - } - - private async markAvailableTasksAsClaimed({ - claimOwnershipUntil, - size, - taskTypes, - }: OwnershipClaimingOpts): Promise { - const { taskTypesToSkip = [], taskTypesToClaim = [] } = groupBy( - this.definitions.getAllTypes(), - (type) => - taskTypes.has(type) && !this.isTaskTypeExcluded(type) - ? 'taskTypesToClaim' - : 'taskTypesToSkip' - ); - const queryForScheduledTasks = mustBeAllOf( - // Task must be enabled - EnabledTask, - // Either a task with idle status and runAt <= now or - // status running or claiming with a retryAt <= now. - shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt) - ); - - const sort: NonNullable = [SortByRunAtAndRetryAt]; - const query = matchesClauses(queryForScheduledTasks, filterDownBy(InactiveTasks)); - const script = updateFieldsAndMarkAsFailed({ - fieldUpdates: { - ownerId: this.taskStore.taskManagerId, - retryAt: claimOwnershipUntil, - }, - claimableTaskTypes: taskTypesToClaim, - skippedTaskTypes: taskTypesToSkip, - unusedTaskTypes: this.unusedTypes, - taskMaxAttempts: pick(this.taskMaxAttempts, taskTypesToClaim), - }); - - const apmTrans = apm.startTransaction( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - - try { - const result = await this.taskStore.updateByQuery( - { - query, - script, - sort, - }, - { - max_docs: size, - } - ); - apmTrans.end('success'); - return result; - } catch (err) { - apmTrans.end('failure'); - throw err; - } - } - - /** - * Fetches tasks from the index, which are owned by the current Kibana instance - */ - private async sweepForClaimedTasks( - taskTypes: Set, - size: number - ): Promise { - const claimedTasksQuery = tasksClaimedByOwner( - this.taskStore.taskManagerId, - tasksOfType([...taskTypes]) - ); - const { docs } = await this.taskStore.fetch({ - query: claimedTasksQuery, - size, - sort: SortByRunAtAndRetryAt, - seq_no_primary_term: true, - }); - - return docs; - } } -const emptyClaimOwnershipResult = () => { - return { - stats: { - tasksUpdated: 0, - tasksConflicted: 0, - tasksClaimed: 0, - tasksRejected: 0, - }, - docs: [], - }; -}; - -function accumulateClaimOwnershipResults( - prev: ClaimOwnershipResult = emptyClaimOwnershipResult(), - next?: ClaimOwnershipResult -) { - if (next) { - const { stats, docs, timing } = next; - const res = { - stats: { - tasksUpdated: stats.tasksUpdated + prev.stats.tasksUpdated, - tasksConflicted: stats.tasksConflicted + prev.stats.tasksConflicted, - tasksClaimed: stats.tasksClaimed + prev.stats.tasksClaimed, - }, - docs, - timing, - }; - return res; - } - return prev; -} - -function isLimited( +export function isLimited( batch: TaskClaimingBatch ): batch is LimitedBatch { return batch.concurrency === BatchConcurrency.Limited; } + function asLimited(tasksType: string): LimitedBatch { return { concurrency: BatchConcurrency.Limited, tasksTypes: tasksType, }; } + function asUnlimited(tasksTypes: Set): UnlimitedBatch { return { concurrency: BatchConcurrency.Unlimited, diff --git a/x-pack/plugins/task_manager/server/task_claimers/README.md b/x-pack/plugins/task_manager/server/task_claimers/README.md new file mode 100644 index 0000000000000..0c92f02031d2e --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_claimers/README.md @@ -0,0 +1,20 @@ +task_claimers +======================================================================== + +This directory contains code that claims the next tasks to run. + +The code is structured to support multiple strategies, but currently +only supports a `default` strategy. + + +`default` task claiming strategy +------------------------------------------------------------------------ +This has been the strategy for task manager for ... ever? The basic +idea: + +- Run an update by query, for number of available workers, to "mark" + task documents as claimed, by setting task state to `claiming`. + We can do some limited per-task logic in that update script. + +- A search is then run on the documents updated from the update by + query. diff --git a/x-pack/plugins/task_manager/server/task_claimers/index.test.ts b/x-pack/plugins/task_manager/server/task_claimers/index.test.ts new file mode 100644 index 0000000000000..be26f0d2f9efb --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_claimers/index.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getTaskClaimer } from '.'; +import { claimAvailableTasksDefault } from './strategy_default'; + +describe('task_claimers/index', () => { + describe('getTaskClaimer()', () => { + test('returns expected result for default', () => { + const taskClaimer = getTaskClaimer('default'); + expect(taskClaimer).toBe(claimAvailableTasksDefault); + }); + + test('throws error for unsupported parameter', () => { + expect(() => getTaskClaimer('not-supported')).toThrowErrorMatchingInlineSnapshot( + `"Unknown task claiming strategy (not-supported)"` + ); + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/task_claimers/index.ts b/x-pack/plugins/task_manager/server/task_claimers/index.ts new file mode 100644 index 0000000000000..8074197a147b6 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_claimers/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Subject, Observable } from 'rxjs'; + +import { TaskStore } from '../task_store'; +import { TaskClaim, TaskTiming } from '../task_events'; +import { TaskTypeDictionary } from '../task_type_dictionary'; +import { TaskClaimingBatches } from '../queries/task_claiming'; +import { ConcreteTaskInstance } from '../task'; +import { claimAvailableTasksDefault } from './strategy_default'; +import { CLAIM_STRATEGY_DEFAULT } from '../config'; + +export interface TaskClaimerOpts { + getCapacity: (taskType?: string | undefined) => number; + claimOwnershipUntil: Date; + batches: TaskClaimingBatches; + events$: Subject; + taskStore: TaskStore; + definitions: TaskTypeDictionary; + unusedTypes: string[]; + excludedTaskTypes: string[]; + taskMaxAttempts: Record; +} + +export interface ClaimOwnershipResult { + stats: { + tasksUpdated: number; + tasksConflicted: number; + tasksClaimed: number; + }; + docs: ConcreteTaskInstance[]; + timing?: TaskTiming; +} + +export type TaskClaimerFn = (opts: TaskClaimerOpts) => Observable; + +export function getTaskClaimer(strategy: string): TaskClaimerFn { + switch (strategy) { + case CLAIM_STRATEGY_DEFAULT: + return claimAvailableTasksDefault; + } + throw new Error(`Unknown task claiming strategy (${strategy})`); +} diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts new file mode 100644 index 0000000000000..c89ecdf669218 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts @@ -0,0 +1,1318 @@ +/* + * Copyright 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 _ from 'lodash'; +import { v1 as uuidv1, v4 as uuidv4 } from 'uuid'; +import { filter, take, toArray } from 'rxjs/operators'; + +import { TaskStatus, ConcreteTaskInstance } from '../task'; +import { SearchOpts, StoreOpts, UpdateByQueryOpts, UpdateByQuerySearchOpts } from '../task_store'; +import { asTaskClaimEvent, TaskEvent } from '../task_events'; +import { asOk, isOk, unwrap } from '../lib/result_type'; +import { TaskTypeDictionary } from '../task_type_dictionary'; +import type { MustNotCondition } from '../queries/query_clauses'; +import { mockLogger } from '../test_utils'; +import { + TaskClaiming, + OwnershipClaimingOpts, + TaskClaimingOpts, + TASK_MANAGER_MARK_AS_CLAIMED, +} from '../queries/task_claiming'; +import { Observable } from 'rxjs'; +import { taskStoreMock } from '../task_store.mock'; +import apm from 'elastic-apm-node'; +import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; +import { ClaimOwnershipResult } from '.'; +import { FillPoolResult } from '../lib/fill_pool'; + +jest.mock('../constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: [ + 'limitedToZero', + 'limitedToOne', + 'anotherLimitedToZero', + 'anotherLimitedToOne', + 'limitedToTwo', + 'limitedToFive', + ], +})); + +const taskManagerLogger = mockLogger(); + +beforeEach(() => jest.clearAllMocks()); + +const mockedDate = new Date('2019-02-12T21:01:22.479Z'); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(global as any).Date = class Date { + constructor() { + return mockedDate; + } + static now() { + return mockedDate.getTime(); + } +}; + +const taskDefinitions = new TaskTypeDictionary(taskManagerLogger); +taskDefinitions.registerTaskDefinitions({ + report: { + title: 'report', + createTaskRunner: jest.fn(), + }, + dernstraight: { + title: 'dernstraight', + createTaskRunner: jest.fn(), + }, + yawn: { + title: 'yawn', + createTaskRunner: jest.fn(), + }, +}); + +const mockApmTrans = { + end: jest.fn(), +}; + +describe('TaskClaiming', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest + .spyOn(apm, 'startTransaction') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(() => mockApmTrans as any); + }); + + describe('claimAvailableTasks', () => { + function initialiseTestClaiming({ + storeOpts = {}, + taskClaimingOpts = {}, + hits = [generateFakeTasks(1)], + versionConflicts = 2, + excludedTaskTypes = [], + unusedTaskTypes = [], + }: { + storeOpts: Partial; + taskClaimingOpts: Partial; + hits?: ConcreteTaskInstance[][]; + versionConflicts?: number; + excludedTaskTypes?: string[]; + unusedTaskTypes?: string[]; + }) { + const definitions = storeOpts.definitions ?? taskDefinitions; + const store = taskStoreMock.create({ taskManagerId: storeOpts.taskManagerId }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + if (hits.length === 1) { + store.fetch.mockResolvedValue({ docs: hits[0] }); + store.updateByQuery.mockResolvedValue({ + updated: hits[0].length, + version_conflicts: versionConflicts, + total: hits[0].length, + }); + } else { + for (const docs of hits) { + store.fetch.mockResolvedValueOnce({ docs }); + store.updateByQuery.mockResolvedValueOnce({ + updated: docs.length, + version_conflicts: versionConflicts, + total: docs.length, + }); + } + } + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: 'default', + definitions, + taskStore: store, + excludedTaskTypes, + unusedTypes: unusedTaskTypes, + maxAttempts: taskClaimingOpts.maxAttempts ?? 2, + getCapacity: taskClaimingOpts.getCapacity ?? (() => 10), + ...taskClaimingOpts, + }); + + return { taskClaiming, store }; + } + + async function testClaimAvailableTasks({ + storeOpts = {}, + taskClaimingOpts = {}, + claimingOpts, + hits = [generateFakeTasks(1)], + versionConflicts = 2, + excludedTaskTypes = [], + unusedTaskTypes = [], + }: { + storeOpts: Partial; + taskClaimingOpts: Partial; + claimingOpts: Omit; + hits?: ConcreteTaskInstance[][]; + versionConflicts?: number; + excludedTaskTypes?: string[]; + unusedTaskTypes?: string[]; + }) { + const getCapacity = taskClaimingOpts.getCapacity ?? (() => 10); + const { taskClaiming, store } = initialiseTestClaiming({ + storeOpts, + taskClaimingOpts, + excludedTaskTypes, + unusedTaskTypes, + hits, + versionConflicts, + }); + + const resultsOrErr = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable(claimingOpts) + ); + for (const resultOrErr of resultsOrErr) { + if (!isOk(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + } + + const results = resultsOrErr.map((resultOrErr) => { + if (!isOk(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + return unwrap(resultOrErr) as ClaimOwnershipResult; + }); + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(store.updateByQuery.mock.calls[0][1]).toMatchObject({ + max_docs: getCapacity(), + }); + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: getCapacity() }); + return results.map((result, index) => ({ + result, + args: { + search: store.fetch.mock.calls[index][0] as SearchOpts & { + query: MustNotCondition; + }, + updateByQuery: store.updateByQuery.mock.calls[index] as [ + UpdateByQuerySearchOpts, + UpdateByQueryOpts + ], + }, + })); + } + + test('makes calls to APM as expected when markAvailableTasksAsClaimed throws error', async () => { + const maxAttempts = _.random(2, 43); + const customMaxAttempts = _.random(44, 100); + + const definitions = new TaskTypeDictionary(mockLogger()); + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + createTaskRunner: jest.fn(), + }, + bar: { + title: 'bar', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + }); + + const { taskClaiming, store } = initialiseTestClaiming({ + storeOpts: { + definitions, + }, + taskClaimingOpts: { + maxAttempts, + }, + }); + + store.updateByQuery.mockRejectedValue(new Error('Oh no')); + + await expect( + getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ + claimOwnershipUntil: new Date(), + }) + ) + ).rejects.toMatchInlineSnapshot(`[Error: Oh no]`); + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); + }); + + test('it filters claimed tasks down by supported types, maxAttempts, status, and runAt', async () => { + const maxAttempts = _.random(2, 43); + const customMaxAttempts = _.random(44, 100); + + const definitions = new TaskTypeDictionary(mockLogger()); + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + createTaskRunner: jest.fn(), + }, + bar: { + title: 'bar', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + foobar: { + title: 'foobar', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + }); + + const [ + { + args: { + updateByQuery: [{ query, sort }], + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + definitions, + }, + taskClaimingOpts: { + maxAttempts, + }, + claimingOpts: { + claimOwnershipUntil: new Date(), + }, + excludedTaskTypes: ['foobar'], + }); + expect(query).toMatchObject({ + bool: { + must: [ + { + bool: { + must: [ + { + term: { + 'task.enabled': true, + }, + }, + ], + }, + }, + { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], + }, + }, + ], + }, + }, + ], + filter: [ + { + bool: { + must_not: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + must: { range: { 'task.retryAt': { gt: 'now' } } }, + }, + }, + ], + }, + }, + ], + }, + }); + expect(sort).toMatchObject([ + { + _script: { + type: 'number', + order: 'asc', + script: { + lang: 'painless', + source: ` +if (doc['task.retryAt'].size()!=0) { + return doc['task.retryAt'].value.toInstant().toEpochMilli(); +} +if (doc['task.runAt'].size()!=0) { + return doc['task.runAt'].value.toInstant().toEpochMilli(); +} + `, + }, + }, + }, + ]); + }); + + test('it should claim in batches partitioned by maxConcurrency', async () => { + const maxAttempts = _.random(2, 43); + const definitions = new TaskTypeDictionary(mockLogger()); + const taskManagerId = uuidv1(); + const fieldUpdates = { + ownerId: taskManagerId, + retryAt: new Date(Date.now()), + }; + definitions.registerTaskDefinitions({ + unlimited: { + title: 'unlimited', + createTaskRunner: jest.fn(), + }, + limitedToZero: { + title: 'limitedToZero', + maxConcurrency: 0, + createTaskRunner: jest.fn(), + }, + anotherUnlimited: { + title: 'anotherUnlimited', + createTaskRunner: jest.fn(), + }, + finalUnlimited: { + title: 'finalUnlimited', + createTaskRunner: jest.fn(), + }, + limitedToOne: { + title: 'limitedToOne', + maxConcurrency: 1, + createTaskRunner: jest.fn(), + }, + anotherLimitedToOne: { + title: 'anotherLimitedToOne', + maxConcurrency: 1, + createTaskRunner: jest.fn(), + }, + limitedToTwo: { + title: 'limitedToTwo', + maxConcurrency: 2, + createTaskRunner: jest.fn(), + }, + }); + const results = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + definitions, + }, + taskClaimingOpts: { + maxAttempts, + getCapacity: (type) => { + switch (type) { + case 'limitedToOne': + case 'anotherLimitedToOne': + return 1; + case 'limitedToTwo': + return 2; + default: + return 10; + } + }, + }, + claimingOpts: { + claimOwnershipUntil: new Date(), + }, + }); + + expect(results.length).toEqual(4); + + expect(results[0].args.updateByQuery[1].max_docs).toEqual(10); + expect(results[0].args.updateByQuery[0].script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['unlimited', 'anotherUnlimited', 'finalUnlimited'], + skippedTaskTypes: [ + 'limitedToZero', + 'limitedToOne', + 'anotherLimitedToOne', + 'limitedToTwo', + ], + unusedTaskTypes: [], + taskMaxAttempts: { + unlimited: maxAttempts, + }, + }, + }); + + expect(results[1].args.updateByQuery[1].max_docs).toEqual(1); + expect(results[1].args.updateByQuery[0].script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['limitedToOne'], + skippedTaskTypes: [ + 'unlimited', + 'limitedToZero', + 'anotherUnlimited', + 'finalUnlimited', + 'anotherLimitedToOne', + 'limitedToTwo', + ], + taskMaxAttempts: { + limitedToOne: maxAttempts, + }, + }, + }); + + expect(results[2].args.updateByQuery[1].max_docs).toEqual(1); + expect(results[2].args.updateByQuery[0].script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['anotherLimitedToOne'], + skippedTaskTypes: [ + 'unlimited', + 'limitedToZero', + 'anotherUnlimited', + 'finalUnlimited', + 'limitedToOne', + 'limitedToTwo', + ], + taskMaxAttempts: { + anotherLimitedToOne: maxAttempts, + }, + }, + }); + + expect(results[3].args.updateByQuery[1].max_docs).toEqual(2); + expect(results[3].args.updateByQuery[0].script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['limitedToTwo'], + skippedTaskTypes: [ + 'unlimited', + 'limitedToZero', + 'anotherUnlimited', + 'finalUnlimited', + 'limitedToOne', + 'anotherLimitedToOne', + ], + taskMaxAttempts: { + limitedToTwo: maxAttempts, + }, + }, + }); + }); + + test('it should reduce the available capacity from batch to batch', async () => { + const maxAttempts = _.random(2, 43); + const definitions = new TaskTypeDictionary(mockLogger()); + const taskManagerId = uuidv1(); + definitions.registerTaskDefinitions({ + unlimited: { + title: 'unlimited', + createTaskRunner: jest.fn(), + }, + limitedToFive: { + title: 'limitedToFive', + maxConcurrency: 5, + createTaskRunner: jest.fn(), + }, + limitedToTwo: { + title: 'limitedToTwo', + maxConcurrency: 2, + createTaskRunner: jest.fn(), + }, + }); + const results = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + definitions, + }, + taskClaimingOpts: { + maxAttempts, + getCapacity: (type) => { + switch (type) { + case 'limitedToTwo': + return 2; + case 'limitedToFive': + return 5; + default: + return 10; + } + }, + }, + hits: [ + [ + // 7 returned by unlimited query + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + mockInstance({ + taskType: 'unlimited', + }), + ], + // 2 returned by limitedToFive query + [ + mockInstance({ + taskType: 'limitedToFive', + }), + mockInstance({ + taskType: 'limitedToFive', + }), + ], + // 1 reterned by limitedToTwo query + [ + mockInstance({ + taskType: 'limitedToTwo', + }), + ], + ], + claimingOpts: { + claimOwnershipUntil: new Date(), + }, + }); + + expect(results.length).toEqual(3); + + expect(results[0].args.updateByQuery[1].max_docs).toEqual(10); + + // only capacity for 3, even though 5 are allowed + expect(results[1].args.updateByQuery[1].max_docs).toEqual(3); + + // only capacity for 1, even though 2 are allowed + expect(results[2].args.updateByQuery[1].max_docs).toEqual(1); + }); + + test('it shuffles the types claimed in batches to ensure no type starves another', async () => { + const maxAttempts = _.random(2, 43); + const definitions = new TaskTypeDictionary(mockLogger()); + const taskManagerId = uuidv1(); + definitions.registerTaskDefinitions({ + unlimited: { + title: 'unlimited', + createTaskRunner: jest.fn(), + }, + anotherUnlimited: { + title: 'anotherUnlimited', + createTaskRunner: jest.fn(), + }, + finalUnlimited: { + title: 'finalUnlimited', + createTaskRunner: jest.fn(), + }, + limitedToOne: { + title: 'limitedToOne', + maxConcurrency: 1, + createTaskRunner: jest.fn(), + }, + anotherLimitedToOne: { + title: 'anotherLimitedToOne', + maxConcurrency: 1, + createTaskRunner: jest.fn(), + }, + limitedToTwo: { + title: 'limitedToTwo', + maxConcurrency: 2, + createTaskRunner: jest.fn(), + }, + }); + + const { taskClaiming, store } = initialiseTestClaiming({ + storeOpts: { + taskManagerId, + definitions, + }, + taskClaimingOpts: { + maxAttempts, + getCapacity: (type) => { + switch (type) { + case 'limitedToOne': + case 'anotherLimitedToOne': + return 1; + case 'limitedToTwo': + return 2; + default: + return 10; + } + }, + }, + }); + + async function getUpdateByQueryScriptParams() { + return ( + await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ + claimOwnershipUntil: new Date(), + }) + ) + ).map( + (result, index) => + ( + store.updateByQuery.mock.calls[index][0] as { + query: MustNotCondition; + size: number; + sort: string | string[]; + script: { + params: { + [claimableTaskTypes: string]: string[]; + }; + }; + } + ).script.params.claimableTaskTypes + ); + } + + const firstCycle = await getUpdateByQueryScriptParams(); + store.updateByQuery.mockClear(); + const secondCycle = await getUpdateByQueryScriptParams(); + + expect(firstCycle.length).toEqual(4); + expect(secondCycle.length).toEqual(4); + expect(firstCycle).not.toMatchObject(secondCycle); + }); + + test('it passes any unusedTaskTypes to script', async () => { + const maxAttempts = _.random(2, 43); + const customMaxAttempts = _.random(44, 100); + const taskManagerId = uuidv1(); + const fieldUpdates = { + ownerId: taskManagerId, + retryAt: new Date(Date.now()), + }; + const definitions = new TaskTypeDictionary(mockLogger()); + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + createTaskRunner: jest.fn(), + }, + bar: { + title: 'bar', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + foobar: { + title: 'foobar', + maxAttempts: customMaxAttempts, + createTaskRunner: jest.fn(), + }, + }); + + const [ + { + args: { + updateByQuery: [{ query, script }], + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + definitions, + taskManagerId, + }, + taskClaimingOpts: { + maxAttempts, + }, + claimingOpts: { + claimOwnershipUntil: new Date(), + }, + excludedTaskTypes: ['foobar'], + unusedTaskTypes: ['barfoo'], + }); + expect(query).toMatchObject({ + bool: { + must: [ + { + bool: { + must: [ + { + term: { + 'task.enabled': true, + }, + }, + ], + }, + }, + { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], + }, + }, + ], + }, + }, + ], + filter: [ + { + bool: { + must_not: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + must: { range: { 'task.retryAt': { gt: 'now' } } }, + }, + }, + ], + }, + }, + ], + }, + }); + expect(script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['foo', 'bar'], + skippedTaskTypes: ['foobar'], + unusedTaskTypes: ['barfoo'], + taskMaxAttempts: { + bar: customMaxAttempts, + foo: maxAttempts, + }, + }, + }); + }); + + test('it claims tasks by setting their ownerId, status and retryAt', async () => { + const taskManagerId = uuidv1(); + const claimOwnershipUntil = new Date(Date.now()); + const fieldUpdates = { + ownerId: taskManagerId, + retryAt: claimOwnershipUntil, + }; + const [ + { + args: { + updateByQuery: [{ script }], + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + }, + taskClaimingOpts: {}, + claimingOpts: { + claimOwnershipUntil, + }, + }); + expect(script).toMatchObject({ + source: expect.any(String), + lang: 'painless', + params: { + fieldUpdates, + claimableTaskTypes: ['report', 'dernstraight', 'yawn'], + skippedTaskTypes: [], + taskMaxAttempts: { + dernstraight: 2, + report: 2, + yawn: 2, + }, + }, + }); + }); + + test('it filters out running tasks', async () => { + const taskManagerId = uuidv1(); + const claimOwnershipUntil = new Date(Date.now()); + const runAt = new Date(); + const tasks = [ + mockInstance({ + id: 'aaa', + runAt, + taskType: 'yawn', + schedule: undefined, + attempts: 0, + status: TaskStatus.Claiming, + params: { hello: 'world' }, + state: { baby: 'Henhen' }, + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + }), + ]; + const [ + { + result: { docs }, + args: { + search: { query }, + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + }, + taskClaimingOpts: {}, + claimingOpts: { + claimOwnershipUntil, + }, + hits: [tasks], + }); + + expect(query).toMatchObject({ + bool: { + must: [ + { + term: { + 'task.ownerId': taskManagerId, + }, + }, + { term: { 'task.status': 'claiming' } }, + { + bool: { + should: [ + { + term: { + 'task.taskType': 'report', + }, + }, + { + term: { + 'task.taskType': 'dernstraight', + }, + }, + { + term: { + 'task.taskType': 'yawn', + }, + }, + ], + }, + }, + ], + }, + }); + + expect(docs).toMatchObject([ + { + attempts: 0, + id: 'aaa', + schedule: undefined, + params: { hello: 'world' }, + runAt, + scope: ['reporting'], + state: { baby: 'Henhen' }, + status: 'claiming', + taskType: 'yawn', + user: 'jimbo', + ownerId: taskManagerId, + }, + ]); + }); + + test('it returns task objects', async () => { + const taskManagerId = uuidv1(); + const claimOwnershipUntil = new Date(Date.now()); + const runAt = new Date(); + const tasks = [ + mockInstance({ + id: 'aaa', + runAt, + taskType: 'yawn', + schedule: undefined, + attempts: 0, + status: TaskStatus.Claiming, + params: { hello: 'world' }, + state: { baby: 'Henhen' }, + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + }), + mockInstance({ + id: 'bbb', + runAt, + taskType: 'yawn', + schedule: { interval: '5m' }, + attempts: 2, + status: TaskStatus.Claiming, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + }), + ]; + const [ + { + result: { docs }, + args: { + search: { query }, + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + }, + taskClaimingOpts: {}, + claimingOpts: { + claimOwnershipUntil, + }, + hits: [tasks], + }); + + expect(query).toMatchObject({ + bool: { + must: [ + { + term: { + 'task.ownerId': taskManagerId, + }, + }, + { term: { 'task.status': 'claiming' } }, + { + bool: { + should: [ + { + term: { + 'task.taskType': 'report', + }, + }, + { + term: { + 'task.taskType': 'dernstraight', + }, + }, + { + term: { + 'task.taskType': 'yawn', + }, + }, + ], + }, + }, + ], + }, + }); + + expect(docs).toMatchObject([ + { + attempts: 0, + id: 'aaa', + schedule: undefined, + params: { hello: 'world' }, + runAt, + scope: ['reporting'], + state: { baby: 'Henhen' }, + status: 'claiming', + taskType: 'yawn', + user: 'jimbo', + ownerId: taskManagerId, + }, + { + attempts: 2, + id: 'bbb', + schedule: { interval: '5m' }, + params: { shazm: 1 }, + runAt, + scope: ['reporting', 'ceo'], + state: { henry: 'The 8th' }, + status: 'claiming', + taskType: 'yawn', + user: 'dabo', + ownerId: taskManagerId, + }, + ]); + }); + + test('it returns version_conflicts that do not include conflicts that were proceeded against', async () => { + const taskManagerId = uuidv1(); + const claimOwnershipUntil = new Date(Date.now()); + const runAt = new Date(); + const tasks = [ + mockInstance({ + runAt, + taskType: 'foo', + schedule: undefined, + attempts: 0, + status: TaskStatus.Claiming, + params: { hello: 'world' }, + state: { baby: 'Henhen' }, + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + }), + mockInstance({ + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: TaskStatus.Claiming, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + }), + ]; + const maxDocs = 10; + const [ + { + result: { + stats: { tasksUpdated, tasksConflicted, tasksClaimed }, + }, + }, + ] = await testClaimAvailableTasks({ + storeOpts: { + taskManagerId, + }, + taskClaimingOpts: { getCapacity: () => maxDocs }, + claimingOpts: { + claimOwnershipUntil, + }, + hits: [tasks], + // assume there were 20 version conflists, but thanks to `conflicts="proceed"` + // we proceeded to claim tasks + versionConflicts: 20, + }); + + expect(tasksUpdated).toEqual(2); + // ensure we only count conflicts that *may* have counted against max_docs, no more than that + expect(tasksConflicted).toEqual(10 - tasksUpdated!); + expect(tasksClaimed).toEqual(2); + }); + }); + + describe('task events', () => { + function generateTasks(taskManagerId: string) { + const runAt = new Date(); + const tasks = [ + { + id: 'claimed-by-id', + runAt, + taskType: 'foo', + schedule: undefined, + attempts: 0, + status: TaskStatus.Claiming, + params: { hello: 'world' }, + state: { baby: 'Henhen' }, + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + traceparent: 'parent', + }, + { + id: 'claimed-by-schedule', + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: TaskStatus.Claiming, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + traceparent: 'newParent', + }, + { + id: 'already-running', + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: TaskStatus.Running, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + traceparent: '', + }, + ]; + + return { taskManagerId, runAt, tasks }; + } + + function instantiateStoreWithMockedApiResponses({ + taskManagerId = uuidv4(), + definitions = taskDefinitions, + getCapacity = () => 10, + tasksClaimed, + }: Partial> & { + taskManagerId?: string; + tasksClaimed?: ConcreteTaskInstance[][]; + } = {}) { + const { runAt, tasks: generatedTasks } = generateTasks(taskManagerId); + const taskCycles = tasksClaimed ?? [generatedTasks]; + + const taskStore = taskStoreMock.create({ taskManagerId }); + taskStore.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + for (const docs of taskCycles) { + taskStore.fetch.mockResolvedValueOnce({ docs }); + taskStore.updateByQuery.mockResolvedValueOnce({ + updated: docs.length, + version_conflicts: 0, + total: docs.length, + }); + } + + taskStore.fetch.mockResolvedValue({ docs: [] }); + taskStore.updateByQuery.mockResolvedValue({ + updated: 0, + version_conflicts: 0, + total: 0, + }); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: 'default', + definitions, + excludedTaskTypes: [], + unusedTypes: [], + taskStore, + maxAttempts: 2, + getCapacity, + }); + + return { taskManagerId, runAt, taskClaiming }; + } + + test('emits an event when a task is succesfully by scheduling', async () => { + const { taskManagerId, runAt, taskClaiming } = instantiateStoreWithMockedApiResponses(); + + const promise = taskClaiming.events + .pipe( + filter( + (event: TaskEvent) => event.id === 'claimed-by-schedule' + ), + take(1) + ) + .toPromise(); + + await getFirstAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ + claimOwnershipUntil: new Date(), + }) + ); + + const event = await promise; + expect(event).toMatchObject( + asTaskClaimEvent( + 'claimed-by-schedule', + asOk({ + id: 'claimed-by-schedule', + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: 'claiming' as TaskStatus, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + traceparent: 'newParent', + }) + ) + ); + }); + }); +}); + +function generateFakeTasks(count: number = 1) { + return _.times(count, (index) => mockInstance({ id: `task:id-${index}` })); +} + +function mockInstance(instance: Partial = {}) { + return Object.assign( + { + id: uuidv4(), + taskType: 'bar', + sequenceNumber: 32, + primaryTerm: 32, + runAt: new Date(), + scheduledAt: new Date(), + startedAt: null, + retryAt: null, + attempts: 0, + params: {}, + scope: ['reporting'], + state: {}, + status: 'idle', + user: 'example', + ownerId: null, + traceparent: '', + }, + instance + ); +} + +function getFirstAsPromise(obs$: Observable): Promise { + return new Promise((resolve, reject) => { + obs$.subscribe(resolve, reject); + }); +} +function getAllAsPromise(obs$: Observable): Promise { + return new Promise((resolve, reject) => { + obs$.pipe(toArray()).subscribe(resolve, reject); + }); +} diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_default.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.ts new file mode 100644 index 0000000000000..0d9ccb2ef723d --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.ts @@ -0,0 +1,255 @@ +/* + * Copyright 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. + */ + +/* + * This module contains helpers for managing the task manager storage layer. + */ +import apm from 'elastic-apm-node'; +import minimatch from 'minimatch'; +import { Subject, Observable, from, of } from 'rxjs'; +import { mergeScan } from 'rxjs/operators'; +import { groupBy, pick } from 'lodash'; + +import { asOk } from '../lib/result_type'; +import { TaskTypeDictionary } from '../task_type_dictionary'; +import { TaskClaimerOpts, ClaimOwnershipResult } from '.'; +import { ConcreteTaskInstance } from '../task'; +import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; +import { isLimited, TASK_MANAGER_MARK_AS_CLAIMED } from '../queries/task_claiming'; +import { TaskClaim, asTaskClaimEvent, startTaskTimer } from '../task_events'; +import { shouldBeOneOf, mustBeAllOf, filterDownBy, matchesClauses } from '../queries/query_clauses'; + +import { + updateFieldsAndMarkAsFailed, + IdleTaskWithExpiredRunAt, + InactiveTasks, + RunningOrClaimingTaskWithExpiredRetryAt, + SortByRunAtAndRetryAt, + tasksClaimedByOwner, + tasksOfType, + EnabledTask, +} from '../queries/mark_available_tasks_as_claimed'; + +import { + correctVersionConflictsForContinuation, + TaskStore, + UpdateByQueryResult, + SearchOpts, +} from '../task_store'; + +interface OwnershipClaimingOpts { + claimOwnershipUntil: Date; + size: number; + taskTypes: Set; + taskStore: TaskStore; + events$: Subject; + definitions: TaskTypeDictionary; + unusedTypes: string[]; + excludedTaskTypes: string[]; + taskMaxAttempts: Record; +} + +export function claimAvailableTasksDefault( + opts: TaskClaimerOpts +): Observable { + const { getCapacity, claimOwnershipUntil, batches, events$, taskStore } = opts; + const { definitions, unusedTypes, excludedTaskTypes, taskMaxAttempts } = opts; + const initialCapacity = getCapacity(); + return from(batches).pipe( + mergeScan( + (accumulatedResult, batch) => { + const stopTaskTimer = startTaskTimer(); + const capacity = Math.min( + initialCapacity - accumulatedResult.stats.tasksClaimed, + isLimited(batch) ? getCapacity(batch.tasksTypes) : getCapacity() + ); + // if we have no more capacity, short circuit here + if (capacity <= 0) { + return of(accumulatedResult); + } + return from( + executeClaimAvailableTasks({ + claimOwnershipUntil, + size: capacity, + events$, + taskTypes: isLimited(batch) ? new Set([batch.tasksTypes]) : batch.tasksTypes, + taskStore, + definitions, + unusedTypes, + excludedTaskTypes, + taskMaxAttempts, + }).then((result) => { + const { stats, docs } = accumulateClaimOwnershipResults(accumulatedResult, result); + stats.tasksConflicted = correctVersionConflictsForContinuation( + stats.tasksClaimed, + stats.tasksConflicted, + initialCapacity + ); + return { stats, docs, timing: stopTaskTimer() }; + }) + ); + }, + // initialise the accumulation with no results + accumulateClaimOwnershipResults(), + // only run one batch at a time + 1 + ) + ); +} + +async function executeClaimAvailableTasks( + opts: OwnershipClaimingOpts +): Promise { + const { taskStore, size, taskTypes, events$ } = opts; + const { updated: tasksUpdated, version_conflicts: tasksConflicted } = + await markAvailableTasksAsClaimed(opts); + + const docs = tasksUpdated > 0 ? await sweepForClaimedTasks(taskStore, taskTypes, size) : []; + + emitEvents( + events$, + docs.map((doc) => asTaskClaimEvent(doc.id, asOk(doc))) + ); + + const stats = { + tasksUpdated, + tasksConflicted, + tasksClaimed: docs.length, + }; + + return { + stats, + docs, + }; +} + +function emitEvents(events$: Subject, events: TaskClaim[]) { + events.forEach((event) => events$.next(event)); +} + +function isTaskTypeExcluded(excludedTaskTypes: string[], taskType: string) { + for (const excludedType of excludedTaskTypes) { + if (minimatch(taskType, excludedType)) { + return true; + } + } + + return false; +} + +async function markAvailableTasksAsClaimed({ + definitions, + excludedTaskTypes, + taskStore, + claimOwnershipUntil, + size, + taskTypes, + unusedTypes, + taskMaxAttempts, +}: OwnershipClaimingOpts): Promise { + const { taskTypesToSkip = [], taskTypesToClaim = [] } = groupBy( + definitions.getAllTypes(), + (type) => + taskTypes.has(type) && !isTaskTypeExcluded(excludedTaskTypes, type) + ? 'taskTypesToClaim' + : 'taskTypesToSkip' + ); + const queryForScheduledTasks = mustBeAllOf( + // Task must be enabled + EnabledTask, + // Either a task with idle status and runAt <= now or + // status running or claiming with a retryAt <= now. + shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt) + ); + + const sort: NonNullable = [SortByRunAtAndRetryAt]; + const query = matchesClauses(queryForScheduledTasks, filterDownBy(InactiveTasks)); + const script = updateFieldsAndMarkAsFailed({ + fieldUpdates: { + ownerId: taskStore.taskManagerId, + retryAt: claimOwnershipUntil, + }, + claimableTaskTypes: taskTypesToClaim, + skippedTaskTypes: taskTypesToSkip, + unusedTaskTypes: unusedTypes, + taskMaxAttempts: pick(taskMaxAttempts, taskTypesToClaim), + }); + + const apmTrans = apm.startTransaction( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + + try { + const result = await taskStore.updateByQuery( + { + query, + script, + sort, + }, + { + max_docs: size, + } + ); + apmTrans.end('success'); + return result; + } catch (err) { + apmTrans.end('failure'); + throw err; + } +} + +async function sweepForClaimedTasks( + taskStore: TaskStore, + taskTypes: Set, + size: number +): Promise { + const claimedTasksQuery = tasksClaimedByOwner( + taskStore.taskManagerId, + tasksOfType([...taskTypes]) + ); + const { docs } = await taskStore.fetch({ + query: claimedTasksQuery, + size, + sort: SortByRunAtAndRetryAt, + seq_no_primary_term: true, + }); + + return docs; +} + +function emptyClaimOwnershipResult() { + return { + stats: { + tasksUpdated: 0, + tasksConflicted: 0, + tasksClaimed: 0, + tasksRejected: 0, + }, + docs: [], + }; +} + +function accumulateClaimOwnershipResults( + prev: ClaimOwnershipResult = emptyClaimOwnershipResult(), + next?: ClaimOwnershipResult +) { + if (next) { + const { stats, docs, timing } = next; + const res = { + stats: { + tasksUpdated: stats.tasksUpdated + prev.stats.tasksUpdated, + tasksConflicted: stats.tasksConflicted + prev.stats.tasksConflicted, + tasksClaimed: stats.tasksClaimed + prev.stats.tasksClaimed, + }, + docs, + timing, + }; + return res; + } + return prev; +} diff --git a/x-pack/plugins/threat_intelligence/cypress/e2e/block_list.cy.ts b/x-pack/plugins/threat_intelligence/cypress/e2e/block_list.cy.ts index 63f58acf99484..a6ce796d4f3f1 100644 --- a/x-pack/plugins/threat_intelligence/cypress/e2e/block_list.cy.ts +++ b/x-pack/plugins/threat_intelligence/cypress/e2e/block_list.cy.ts @@ -35,7 +35,8 @@ const FIRST_BLOCK_LIST_NEW_DESCRIPTION = 'the first description'; const SECOND_BLOCK_LIST_NEW_NAME = 'second blocklist entry'; const SECOND_BLOCK_LIST_NEW_DESCRIPTION = 'the second description'; -describe('Block list with invalid indicators', { tags: '@ess' }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/171783 +describe.skip('Block list with invalid indicators', { tags: '@ess' }, () => { beforeEach(() => { esArchiverLoad('threat_intelligence/invalid_indicators_data'); login(); diff --git a/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts b/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts index cbb95e9a610f1..6ad05c0472577 100644 --- a/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts +++ b/x-pack/plugins/threat_intelligence/cypress/e2e/indicators.cy.ts @@ -56,7 +56,11 @@ const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators'; const URL_WITH_CONTRADICTORY_FILTERS = '/app/security/threat_intelligence/indicators?indicators=(filterQuery:(language:kuery,query:%27%27),filters:!((%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:file),type:phrase),query:(match_phrase:(threat.indicator.type:file))),(%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:url),type:phrase),query:(match_phrase:(threat.indicator.type:url)))),timeRange:(from:now/d,to:now/d))'; -describe('Invalid Indicators', { tags: '@ess' }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/171779 +// FLAKY: https://github.com/elastic/kibana/issues/171778 +// FLAKY: https://github.com/elastic/kibana/issues/171785 +// FLAKY: https://github.com/elastic/kibana/issues/171786 +describe.skip('Invalid Indicators', { tags: '@ess' }, () => { describe('verify the grid loads even with missing fields', () => { beforeEach(() => { esArchiverLoad('threat_intelligence/invalid_indicators_data'); @@ -132,7 +136,9 @@ describe('Invalid Indicators', { tags: '@ess' }, () => { }); }); -describe('Indicators', () => { +// FLAKY: https://github.com/elastic/kibana/issues/171781 +// FLAKY: https://github.com/elastic/kibana/issues/171780 +describe.skip('Indicators', () => { before(() => { esArchiverLoad('threat_intelligence/indicators_data'); }); diff --git a/x-pack/plugins/threat_intelligence/public/components/paywall.tsx b/x-pack/plugins/threat_intelligence/public/components/paywall.tsx index a01ca32a06946..8a010550e8a6b 100644 --- a/x-pack/plugins/threat_intelligence/public/components/paywall.tsx +++ b/x-pack/plugins/threat_intelligence/public/components/paywall.tsx @@ -6,17 +6,21 @@ */ import React, { VFC } from 'react'; -import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { + EuiButton, + EuiButtonEmpty, + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SubscriptionButtonEmpty } from '@kbn/subscription-tracking'; -import type { SubscriptionContextData } from '@kbn/subscription-tracking'; - -const subscriptionContext: SubscriptionContextData = { - feature: 'threat-intelligence', - source: 'security__threat-intelligence', -}; +import { useKibana } from '../hooks/use_kibana'; export const Paywall: VFC = () => { + const { + services: { application }, + } = useKibana(); return ( } @@ -52,12 +56,18 @@ export const Paywall: VFC = () => {
    - + + application.navigateToApp('management', { + path: 'stack/license_management/home', + }) + } + > - +
    diff --git a/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx b/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx index d13c3f561e748..249a9d05afbc9 100644 --- a/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/mocks/story_providers.tsx @@ -12,7 +12,6 @@ import { CoreStart, IUiSettingsClient } from '@kbn/core/public'; import { TimelinesUIStart } from '@kbn/timelines-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; -import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import type { SettingsStart } from '@kbn/core-ui-settings-browser'; import { mockIndicatorsFiltersContext } from './mock_indicators_filters_context'; @@ -108,9 +107,7 @@ export const StoryProvidersComponent: VFC = ({ - - {children} - + {children} diff --git a/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx index 12c42052ee26f..37360284b6aa7 100644 --- a/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/mocks/test_providers.tsx @@ -17,7 +17,6 @@ import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks import { createTGridMocks } from '@kbn/timelines-plugin/public/mock'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; -import { MockSubscriptionTrackingProvider } from '@kbn/subscription-tracking/mocks'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { MemoryRouter } from 'react-router-dom'; import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; @@ -142,13 +141,11 @@ export const TestProvidersComponent: FC = ({ children }) => ( - - - - {children} - - - + + + {children} + + diff --git a/x-pack/plugins/threat_intelligence/public/plugin.tsx b/x-pack/plugins/threat_intelligence/public/plugin.tsx index e30e9f92c0a5b..49f6b3b7724bf 100755 --- a/x-pack/plugins/threat_intelligence/public/plugin.tsx +++ b/x-pack/plugins/threat_intelligence/public/plugin.tsx @@ -11,7 +11,6 @@ import { Provider as ReduxStoreProvider } from 'react-redux'; import React, { Suspense } from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { ExternalReferenceAttachmentType } from '@kbn/cases-plugin/public/client/attachment_framework/types'; -import { SubscriptionTrackingProvider } from '@kbn/subscription-tracking'; import { generateAttachmentType } from './modules/cases/utils/attachments'; import { KibanaContextProvider } from './hooks/use_kibana'; import { @@ -44,16 +43,11 @@ export const createApp = - - - }> - - - - + + }> + + + diff --git a/x-pack/plugins/threat_intelligence/tsconfig.json b/x-pack/plugins/threat_intelligence/tsconfig.json index 661186c943b54..2e390483ab22c 100644 --- a/x-pack/plugins/threat_intelligence/tsconfig.json +++ b/x-pack/plugins/threat_intelligence/tsconfig.json @@ -32,8 +32,7 @@ "@kbn/utility-types", "@kbn/ui-theme", "@kbn/securitysolution-io-ts-list-types", - "@kbn/core-ui-settings-browser", - "@kbn/subscription-tracking" + "@kbn/core-ui-settings-browser" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts b/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts index e12c144b60af6..7da139c7b5bd5 100644 --- a/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts +++ b/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts @@ -6,6 +6,7 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import type { DeleteDataViewApiResponseSchema } from '@kbn/ml-data-view-utils/types/api_delete_response_schema'; import { transformStateSchema, ResponseStatus } from './common'; @@ -29,7 +30,7 @@ export type DeleteTransformsRequestSchema = TypeOf; -export const putTransformsQuerySchema = schema.object({ - createDataView: schema.boolean({ defaultValue: false }), - timeFieldName: schema.maybe(schema.string()), -}); - -export type PutTransformsQuerySchema = TypeOf; - interface TransformCreated { transform: TransformId; } -interface DataViewCreated { - id: string; -} interface CreatedError { id: string; error: any; } -export interface PutTransformsResponseSchema { +export interface PutTransformsResponseSchema extends CreateDataViewApiResponseSchema { transformsCreated: TransformCreated[]; - dataViewsCreated: DataViewCreated[]; - dataViewsErrors: CreatedError[]; errors: CreatedError[]; } diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index f88ef1e39d446..9f2cf91b088b1 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -146,7 +146,7 @@ export const useDeleteTransforms = () => { } if (status.destDataViewDeleted?.error) { - const error = status.destDataViewDeleted.error.reason; + const error = extractErrorMessage(status.destDataViewDeleted.error); toastNotifications.addDanger({ title: i18n.translate( 'xpack.transform.deleteTransform.deleteAnalyticsWithDataViewErrorMessage', diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index 471146f583dc8..61403df035090 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -17,7 +17,7 @@ import { getFieldType, getDataGridSchemaFromKibanaFieldType, getDataGridSchemaFromESFieldType, - getFieldsFromKibanaIndexPattern, + getFieldsFromKibanaDataView, showDataGridColumnChartErrorMessageToast, useDataGrid, useRenderCellValue, @@ -140,7 +140,7 @@ export const useIndexData = ( allPopulatedFields = [...new Set(docs.map(Object.keys).flat(1))]; } - const allDataViewFields = getFieldsFromKibanaIndexPattern(dataView); + const allDataViewFields = getFieldsFromKibanaDataView(dataView); return allPopulatedFields.filter((d) => allDataViewFields.includes(d)).sort(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [dataViewFieldsData, populatedFields]); 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 0d93a31f9e3f2..865614400b6e8 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 @@ -21,12 +21,13 @@ import { EuiSelect, EuiSpacer, EuiCallOut, - EuiText, EuiTextArea, } from '@elastic/eui'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { toMountPoint } from '@kbn/react-kibana-mount'; +import { CreateDataViewForm } from '@kbn/ml-data-view-utils/components/create_data_view_form_row'; +import { DestinationIndexForm } from '@kbn/ml-creation-wizard-utils/components/destination_index_form'; import { retentionPolicyMaxAgeInvalidErrorMessage } from '../../../../common/constants/validation_messages'; import { DEFAULT_TRANSFORM_FREQUENCY } from '../../../../../../common/constants'; @@ -46,7 +47,6 @@ import { useGetTransformsPreview, } from '../../../../hooks'; import { SearchItems } from '../../../../hooks/use_search_items'; -import { StepDetailsTimeField } from './step_details_time_field'; import { getTransformConfigQuery, getPreviewTransformRequestBody, @@ -88,6 +88,9 @@ export const StepDetailsForm: FC = React.memo( const [destinationIndex, setDestinationIndex] = useState( defaults.destinationIndex ); + const [destIndexSameAsId, setDestIndexSameAsId] = useState( + destinationIndex !== undefined && destinationIndex === transformId + ); const [destinationIngestPipeline, setDestinationIngestPipeline] = useState( defaults.destinationIngestPipeline ); @@ -378,6 +381,13 @@ export const StepDetailsForm: FC = React.memo( /* eslint-enable react-hooks/exhaustive-deps */ ]); + useEffect(() => { + if (destIndexSameAsId === true && !transformIdEmpty && transformIdValid) { + setDestinationIndex(transformId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [destIndexSameAsId, transformId]); + return (
    @@ -439,51 +449,31 @@ export const StepDetailsForm: FC = React.memo( /> - - {i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', { - defaultMessage: 'Invalid destination index name.', - })} -
    - - {i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink', - { - defaultMessage: 'Learn more about index name limitations.', - } - )} - - , - ] - } - > - setDestinationIndex(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel', - { - defaultMessage: 'Choose a unique destination index name.', - } - )} - isInvalid={!indexNameEmpty && !indexNameValid} - data-test-subj="transformDestinationIndexInput" - /> -
    + } + )} + isJobCreated={transformIdExists} + onDestinationIndexChange={setDestinationIndex} + setDestIndexSameAsId={setDestIndexSameAsId} + switchLabel={i18n.translate( + 'xpack.transform.stepDetailsForm.destinationIndexFormSwitchLabel', + { + defaultMessage: 'Use transform ID as destination index name', + } + )} + /> {ingestPipelineNames.length > 0 && ( = React.memo( ) : null} - - {i18n.translate('xpack.transform.stepDetailsForm.dataViewPermissionWarning', { - defaultMessage: 'You need permission to create data views.', - })} - , - ] - : []), - ...(createDataView && dataViewTitleExists - ? [ - i18n.translate('xpack.transform.stepDetailsForm.dataViewTitleError', { - defaultMessage: 'A data view with this title already exists.', - }), - ] - : []), - ]} - > - setCreateDataView(!createDataView)} - data-test-subj="transformCreateDataViewSwitch" - /> - - {createDataView && !dataViewTitleExists && dataViewAvailableTimeFields.length > 0 && ( - - )} + {/* Continuous mode */} title === indexName); - return dv?.id; -} - -async function deleteDestDataViewById(dataViewId: string, dataViewsService: DataViewsService) { - return await dataViewsService.delete(dataViewId); -} - export async function deleteTransforms( reqBody: DeleteTransformsRequestSchema, ctx: RequestHandlerContext, @@ -50,7 +43,7 @@ export async function deleteTransforms( const transformDeleted: ResponseStatus = { success: false }; const destIndexDeleted: ResponseStatus = { success: false }; - const destDataViewDeleted: ResponseStatus = { + let destDataViewDeleted: DeleteDataViewApiResponseSchema = { success: false, }; const transformId = transformInfo.id; @@ -84,15 +77,10 @@ export async function deleteTransforms( // Delete the data view if there's a data view that matches the name of dest index if (destinationIndex && deleteDestDataView) { - try { - const dataViewId = await getDataViewId(destinationIndex, dataViewsService); - if (dataViewId) { - await deleteDestDataViewById(dataViewId, dataViewsService); - destDataViewDeleted.success = true; - } - } catch (deleteDestDataViewError) { - destDataViewDeleted.error = deleteDestDataViewError.meta.body.error; - } + destDataViewDeleted = await deleteDataViewFn({ + dataViewsService, + dataViewName: destinationIndex, + }); } try { diff --git a/x-pack/plugins/transform/server/routes/api/transforms_create/register_route.ts b/x-pack/plugins/transform/server/routes/api/transforms_create/register_route.ts index 49b8e7c138357..f6be9e18893f2 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms_create/register_route.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms_create/register_route.ts @@ -5,15 +5,18 @@ * 2.0. */ +import { + dataViewCreateQuerySchema, + type DataViewCreateQuerySchema, +} from '@kbn/ml-data-view-utils/schemas/api_create_query_schema'; + import { transformIdParamSchema, type TransformIdParamSchema, } from '../../../../common/api_schemas/common'; import { putTransformsRequestSchema, - putTransformsQuerySchema, type PutTransformsRequestSchema, - type PutTransformsQuerySchema, } from '../../../../common/api_schemas/transforms'; import { addInternalBasePath } from '../../../../common/constants'; @@ -40,20 +43,20 @@ export function registerRoute(routeDependencies: RouteDependencies) { path: addInternalBasePath('transforms/{transformId}'), access: 'internal', }) - .addVersion( + .addVersion( { version: '1', validate: { request: { params: transformIdParamSchema, - query: putTransformsQuerySchema, + query: dataViewCreateQuerySchema, body: putTransformsRequestSchema, }, }, }, license.guardApiRoute< TransformIdParamSchema, - PutTransformsQuerySchema, + DataViewCreateQuerySchema, PutTransformsRequestSchema >(routeHandlerFactory(routeDependencies)) ); diff --git a/x-pack/plugins/transform/server/routes/api/transforms_create/route_handler_factory.ts b/x-pack/plugins/transform/server/routes/api/transforms_create/route_handler_factory.ts index 1679bbf3a6309..8ca98efe3cb54 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms_create/route_handler_factory.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms_create/route_handler_factory.ts @@ -7,12 +7,13 @@ import type { RequestHandler } from '@kbn/core/server'; import type { RuntimeField } from '@kbn/data-views-plugin/common'; +import type { DataViewCreateQuerySchema } from '@kbn/ml-data-view-utils/schemas/api_create_query_schema'; +import { createDataViewFn } from '@kbn/ml-data-view-utils/actions/create'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { TransformIdParamSchema } from '../../../../common/api_schemas/common'; import type { PutTransformsRequestSchema, - PutTransformsQuerySchema, PutTransformsResponseSchema, } from '../../../../common/api_schemas/transforms'; import { isLatestTransform } from '../../../../common/types/transform'; @@ -26,7 +27,7 @@ export const routeHandlerFactory: ( routeDependencies: RouteDependencies ) => RequestHandler< TransformIdParamSchema, - PutTransformsQuerySchema, + DataViewCreateQuerySchema, PutTransformsRequestSchema, TransformRequestHandlerContext > = (routeDependencies) => async (ctx, req, res) => { @@ -73,35 +74,23 @@ export const routeHandlerFactory: ( req ); - const dataViewName = req.body.dest.index; const runtimeMappings = req.body.source.runtime_mappings as Record; - try { - const dataViewsResp = await dataViewsService.createAndSave( - { - title: dataViewName, - timeFieldName, - // Adding runtime mappings for transforms of type latest only here - // since only they will want to replicate the source index mapping. - // Pivot type transforms have index mappings that cannot be - // inferred from the source index. - ...(isPopulatedObject(runtimeMappings) && isLatestTransform(req.body) - ? { runtimeFieldMap: runtimeMappings } - : {}), - allowNoIndex: true, - }, - false, - true - ); + const { dataViewsCreated, dataViewsErrors } = await createDataViewFn({ + dataViewsService, + dataViewName: req.body.dest.index, + // Adding runtime mappings for transforms of type latest only here + // since only they will want to replicate the source index mapping. + // Pivot type transforms have index mappings that cannot be + // inferred from the source index. + runtimeMappings: + isPopulatedObject(runtimeMappings) && isLatestTransform(req.body) ? runtimeMappings : {}, + timeFieldName, + errorFallbackId: transformId, + }); - if (dataViewsResp.id) { - response.dataViewsCreated = [{ id: dataViewsResp.id }]; - } - } catch (error) { - // For the error id we use the transform id - // because in case of an error we don't get a data view id. - response.dataViewsErrors = [{ id: transformId, error }]; - } + response.dataViewsCreated = dataViewsCreated; + response.dataViewsErrors = dataViewsErrors; } return res.ok({ body: response }); diff --git a/x-pack/plugins/transform/tsconfig.json b/x-pack/plugins/transform/tsconfig.json index d58a875eec9c5..b927516b7ee55 100644 --- a/x-pack/plugins/transform/tsconfig.json +++ b/x-pack/plugins/transform/tsconfig.json @@ -69,7 +69,9 @@ "@kbn/content-management-plugin", "@kbn/react-kibana-mount", "@kbn/core-plugins-server", - "@kbn/data-view-editor-plugin" + "@kbn/data-view-editor-plugin", + "@kbn/ml-data-view-utils", + "@kbn/ml-creation-wizard-utils" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 360be51913f25..2a45559b22b5e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2051,7 +2051,6 @@ "data.search.esErrorTitle": "Impossible d’extraire les résultats de recherche", "data.search.esql.help": "Interroge Elasticsearch avec ES|QL.", "data.search.esql.query.help": "Une recherche ES|QL.", - "data.search.esql.timezone.help": "Fuseau horaire à utiliser pour les opérations de date. Les formats ISO8601 et les décalages UTC valides fonctionnent.", "data.search.essql.count.help": "Nombre de documents à récupérer. Pour de meilleures performances, utilisez un ensemble de données plus petit.", "data.search.essql.help": "Interroge Elasticsearch avec Elasticsearch SQL.", "data.search.essql.locale.help": "Le paramètre régional à utiliser.", @@ -5308,7 +5307,6 @@ "savedSearch.kibana_context.savedSearchId.help": "Spécifier l'ID de recherche enregistrée à utiliser pour les requêtes et les filtres", "savedSearch.kibana_context.timeRange.help": "Spécifier le filtre de plage temporelle Kibana", "searchApiPanels.welcomeBanner.header.greeting.customTitle": "Bonjour {name} !", - "searchApiPanels.welcomeBanner.ingestData.clientDocLink": "Référence d’API {languageName}", "searchApiPanels.welcomeBanner.installClient.clientDocLink": "Documentation du client {languageName}", "searchApiPanels.welcomeBanner.selectClient.description": "Elastic construit et assure la maintenance des clients dans plusieurs langues populaires et notre communauté a contribué à beaucoup d'autres. Sélectionnez votre client linguistique favori or explorez la {console} pour commencer.", "searchApiPanels.welcomeBanner.codeBox.copyButtonLabel": "Copier", @@ -5316,22 +5314,7 @@ "searchApiPanels.welcomeBanner.header.description": "Configurez votre client de langage de programmation, ingérez des données, et vous serez prêt à commencer vos recherches en quelques minutes.", "searchApiPanels.welcomeBanner.header.greeting.defaultTitle": "Bonjour !", "searchApiPanels.welcomeBanner.header.title": "Lancez-vous avec Elasticsearch", - "searchApiPanels.welcomeBanner.ingestData.beatsDescription": "Des agents légers conçus pour le transfert de données pour Elasticsearch. Utilisez Beats pour envoyer des données opérationnelles depuis vos serveurs.", - "searchApiPanels.welcomeBanner.ingestData.beatsLink": "Beats", - "searchApiPanels.welcomeBanner.ingestData.beatsTitle": "Beats", - "searchApiPanels.welcomeBanner.ingestData.connectorsDescription": "Des intégrations spécialisées pour synchroniser des données de sources tierces avec Elasticsearch. Utilisez des connecteurs Elastic pour synchroniser du contenu d’une plage de bases de données et de stockage d’objets.", - "searchApiPanels.welcomeBanner.ingestData.connectorsPythonLink": "connecteurs-python", - "searchApiPanels.welcomeBanner.ingestData.connectorsTitle": "Client de connecteur", "searchApiPanels.welcomeBanner.ingestData.description": "Ajoutez des données à votre flux de données ou à votre index pour les rendre interrogeables. Choisissez une méthode d’ingestion qui correspond à votre application et à votre workflow.", - "searchApiPanels.welcomeBanner.ingestData.ingestApiDescription": "La façon la plus flexible d’indexer des données, ce qui vous donne un contrôle total sur vos options de personnalisation et d’optimisation.", - "searchApiPanels.welcomeBanner.ingestData.ingestApiLabel": "Ingérer via une API", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationDescription": "Des outils d’ingestion spécialisés optimisés pour transformer des données et les transférer à Elasticsearch.", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationLabel": "Ingérer via l’intégration", - "searchApiPanels.welcomeBanner.ingestData.ingestLegendLabel": "Sélectionner une méthode d'ingestion", - "searchApiPanels.welcomeBanner.ingestData.integrationsLink": "À propos des intégrations", - "searchApiPanels.welcomeBanner.ingestData.logstashDescription": "Ajoutez des données à votre flux de données ou à votre index pour les rendre interrogeables. Choisissez une méthode d’ingestion qui correspond à votre application et à votre workflow.", - "searchApiPanels.welcomeBanner.ingestData.logstashLink": "Logstash", - "searchApiPanels.welcomeBanner.ingestData.logstashTitle": "Logstash", "searchApiPanels.welcomeBanner.ingestData.title": "Ingérer des données", "searchApiPanels.welcomeBanner.installClient.description": "Elastic construit et assure la maintenance des clients dans plusieurs langues populaires et notre communauté a contribué à beaucoup d'autres. Installez votre client de langage favori pour commencer.", "searchApiPanels.welcomeBanner.installClient.title": "Installer un client", @@ -12858,7 +12841,6 @@ "xpack.dataVisualizer.table.expandRowScreenMsg": "Développer la ligne", "xpack.dataVisualizer.title": "Charger un fichier", "xpack.elasticAssistant.assistant.connectors.connectorMissingCallout.calloutDescription": "Sélectionnez un connecteur ci-dessus ou depuis {link} pour continuer", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactText": "Anecdotes : Surveillez les logs de serveur Kibana pour visualiser la progression et {funFacts} pour afficher les résultats dans Discover une fois l'opération terminée. Cette opération prendra (plusieurs) minutes, selon la quantité de données. Si vous fermez cette fenêtre, vous interromprez l'évaluation.", "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "Configurez ELSER dans {machineLearning} pour commencer. {seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "Initialisé sur \"{kbIndexPattern}\"", "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "Les réponses des systèmes d'IA ne sont pas toujours tout à fait exactes. Pour en savoir plus sur la fonctionnalité d'assistant et son utilisation, consultez {documentationLink}.", @@ -12986,7 +12968,6 @@ "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationPromptLabel": "Invite d'évaluation", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeDescription": "Type d'évaluation à effectuer, par exemple \"correctness\" \"esql-validator\" ou \"custom\", et fournit votre propre invite d'évaluation", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeLabel": "Type d'évaluation", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetDescription": "Exemple d'ensemble de données à évaluer. Tableau avec des objets aux propriétés \"input\" (entrée) et \"references\" (références)", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetLabel": "Ensemble de données", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactDiscoverLinkText": "cliquez ici", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorModelDescription": "Modèle avec lequel effectuer l'évaluation finale", @@ -13149,7 +13130,6 @@ "xpack.enterpriseSearch.content.index.connector.syncRules.flyout.errorTitle": "{ids} {idsLength, plural, one {règle} many {règles} other {règles}} de synchronisation {idsLength, plural, one {est} many {sont} other {sont}} non valide(s).", "xpack.enterpriseSearch.content.index.pipelines.copyCustomizeCallout.description": "Votre index utilise notre pipeline d'ingestion par défaut {defaultPipeline}. Copiez ce pipeline dans une configuration spécifique à l'index pour déverrouiller la possibilité de créer des pipelines d'ingestion et d'inférence personnalisés.", "xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyAPIText": "{apiIndex} Les modifications apportées aux paramètres ci-dessous sont uniquement fournies à titre indicatif. Ces paramètres ne seront pas conservés dans votre index ou pipeline.", - "xpack.enterpriseSearch.content.indices.callout.text": "Vos index Elasticsearch sont maintenant au premier plan dans Search. Vous pouvez créer des index et lancer directement des expériences de recherche avec ces index. Pour en savoir plus sur l'utilisation des index de Elasticsearch dans Search {docLink}", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.description": "D'abord, générez une clé d'API Elasticsearch. Cette clé {apiKeyName} permet d'activer les autorisations de lecture et d'écriture du connecteur pour qu'il puisse indexer les documents dans l'index {indexName} créé. Enregistrez cette clé en lieu sûr, car vous en aurez besoin pour configurer votre connecteur.", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected": "Votre connecteur {name} s’est bien connecté à Search.", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.secondParagraph": "Le référentiel de connecteurs contient plusieurs {link}. Utilisez notre cadre pour accélérer le développement de connecteurs pour des sources de données personnalisées.", @@ -14365,8 +14345,6 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.done": "Terminé", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.generateButton": "Générer une clé", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "Créer une clé d'API d'analyse", - "xpack.enterpriseSearch.content.callout.dismissButton": "Rejeter", - "xpack.enterpriseSearch.content.callout.title": "Présentation des index Elasticsearch dans Search", "xpack.enterpriseSearch.content.cannotConnect.body": "En savoir plus.", "xpack.enterpriseSearch.content.cannotConnect.title": "Impossible de se connecter à Enterprise Search", "xpack.enterpriseSearch.content.crawler.authentication": "Authentification", @@ -14468,7 +14446,6 @@ "xpack.enterpriseSearch.content.index.syncButton.label": "Sync", "xpack.enterpriseSearch.content.index.syncButton.syncing.label": "Synchronisation en cours", "xpack.enterpriseSearch.content.index.syncButton.waitingForSync.label": "En attente de la synchronisation", - "xpack.enterpriseSearch.content.indices.callout.docLink": "lire la documentation", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.button.label": "Générer une clé API", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.cancelButton.label": "Annuler", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.confirmButton.label": "Générer une clé API", @@ -14602,16 +14579,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.createErrors": "Erreur lors de la création d'un pipeline", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.esDocs.link": "Découvrir comment ajouter un modèle entraîné", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.imageAlt": "Illustration d'absence de modèles de Machine Learning", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.chooseExistingLabel": "Nouveau ou existant", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.description": "Créez ou réutilisez un pipeline enfant qui servira de processeur dans votre pipeline principal.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionDeployTrainedModel": "Pour effectuer des tâches de traitement du langage naturel dans votre cluster, vous devez déployer un modèle entraîné approprié.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionUsePipelines": "Les pipelines que vous créez sont enregistrés pour être utilisés ailleurs dans votre déploiement Elastic.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink": "Découvrez l'importation et l'utilisation des modèles de ML dans Search", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.emptyValueError": "Champ obligatoire.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.chooseLabel": "Choisir", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.existingLabel": "Pipeline existant", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.model": "Modèle", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.newLabel": "Nouveau pipeline", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.placeholder": "Effectuez une sélection", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.sourceFields": "Champs sources", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipelineLabel": "Sélectionner un pipeline d'inférence existant", @@ -14621,11 +14591,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName": "Le nom doit contenir uniquement des lettres, des chiffres, des traits de soulignement et des traits d'union.", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.placeholder": "Sélectionner un modèle", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.redactedValue": "Ce modèle n'est pas disponible dans l'espace Kibana", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.modelLabel": "Sélectionner un modèle de ML entraîné", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.nameLabel": "Nom", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.namePlaceholder": "Saisir un nom unique pour ce pipeline", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.pipelineNameExistsError": "Ce nom est déjà utilisé par un autre pipeline.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.title": "Créer ou sélectionner un pipeline", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.titleSelectTrainedModel": "Sélectionner un modèle de ML entraîné", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions": "Actions", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions.deleteMapping": "Supprimer ce mapping", @@ -14662,15 +14630,6 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.subtitle.result": "Résultat", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.title": "Tester les résultats de votre pipeline", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.useJsonFormat": "Utilisez ce format JSON pour ajouter votre propre tableau de documents", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.description": "Vous devez mettre à jour manuellement vos mappings d'index avant de pouvoir débuter l'indexation des documents à l'aide du pipeline.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.descriptionNoAction": "Vos mappings d'index sont automatiquement mis à jour pour inclure les champs de sortie d'inférence sélectionnés.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.docsLink": "En savoir plus", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappings": "Mappings de champs", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappingsRequired": "Mappings de champs requis", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.presentInMapping": "Assurez-vous que les champs de sortie d'inférence sélectionnés sont présent dans le mapping.", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.required": "Obligatoire", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.title": "Mettez à jour vos mappings d'index", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.titleNoAction": "Consultez les mises à jour des mappings d'index", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.title": "Ajouter un pipeline d'inférence", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.apiIndexSubtitle": "Les pipelines d'ingestion optimisent votre index pour les applications de recherche. Si vous souhaitez utiliser ces pipelines dans votre index basé sur des API, vous devrez les référencer explicitement dans vos requêtes d'API.", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.customBadge": "Personnalisé", @@ -14751,7 +14710,6 @@ "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.fields.title": "Champs", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.review.title": "Révision", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.test.title": "Test (facultatif)", - "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.updateMappings.title": "Mappings", "xpack.enterpriseSearch.content.integrations.dropbox": "Dropbox", "xpack.enterpriseSearch.content.integrations.dropboxDescription": "Effectuez des recherches dans vos fichiers et dossiers stockés sur Dropbox.", "xpack.enterpriseSearch.content.integrations.dropboxPaper": "Dropbox Paper", @@ -15323,7 +15281,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "Extraire, transformer, indexer et synchroniser des données issues d'une source de données tiers.", "xpack.enterpriseSearch.ingestSelector.method.crawler": "Robot d'indexation", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "Découvrir, extraire et indexer du contenu interrogeable provenant de sites web et de bases de connaissances.", - "xpack.enterpriseSearch.ingestSelector.startButton": "Début", "xpack.enterpriseSearch.inlineEditableTable.newRowButtonLabel": "Nouvelle ligne", "xpack.enterpriseSearch.integrations.apiDescription": "Ajouter la recherche à votre application avec les API robustes d'Elasticsearch.", "xpack.enterpriseSearch.integrations.apiName": "API", @@ -17908,7 +17865,6 @@ "xpack.fleet.initializationErrorMessageTitle": "Initialisation de Fleet impossible", "xpack.fleet.integrations.customInputsLink": "entrées personnalisées", "xpack.fleet.integrations.discussForumLink": "forum", - "xpack.fleet.integrations.endpointsButton": "Points de terminaison", "xpack.fleet.integrations.installPackage.uploadedTooltip": "Cette intégration a été installée par le biais d'un chargement et ne peut pas être réinstallée automatiquement. Veuillez la charger à nouveau pour la réinstaller.", "xpack.fleet.integrations.integrationSaved": "Paramètres de l'intégration enregistrés", "xpack.fleet.integrations.integrationSavedError": "Erreur lors de l’enregistrement des paramètres de l'intégration", @@ -18637,9 +18593,7 @@ "xpack.grokDebugger.unknownErrorTitle": "Un problème est survenu", "xpack.idxMgmt.badgeAriaLabel": "{label}. Sélectionnez pour filtrer selon cet élément.", "xpack.idxMgmt.clearCacheIndicesAction.indexCacheClearedMessage": "Le cache de l'index {indexNames} a été effacé.", - "xpack.idxMgmt.clearCacheIndicesAction.successMessage": "Le cache a bien été effacé : [{indexNames}]", "xpack.idxMgmt.closeIndicesAction.indexClosedMessage": "L'index {indexNames} a été fermé.", - "xpack.idxMgmt.closeIndicesAction.successfullyClosedIndicesMessage": "Fermeture réussie : [{indexNames}]", "xpack.idxMgmt.componentTemplateDetails.summaryTab.notInUseDescription": "{createLink} un modèle d'index ou {editLink}-en un existant.", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaDescription": "Informations arbitraires sur le modèle stockées dans l'état du cluster. {learnMoreLink}", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaHelpText": "Utiliser le format JSON : {code}", @@ -18658,7 +18612,6 @@ "xpack.idxMgmt.deleteDataStreamsConfirmationModal.multipleErrorsNotificationMessageText": "Erreur lors de la suppression de {count} flux de données", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, one {# flux de données} many {# flux de données} other {# flux de données}} supprimé", "xpack.idxMgmt.deleteIndicesAction.indexDeletedMessage": "L'index {indexNames} a été supprimé.", - "xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage": "Suppression réussie : [{indexNames}]", "xpack.idxMgmt.deleteTemplatesModal.confirmButtonLabel": "Supprimer {numTemplatesToDelete, plural, one {modèle} many {modèles} other {modèles}}", "xpack.idxMgmt.deleteTemplatesModal.deleteDescription": "Vous êtes sur le point de supprimer {numTemplatesToDelete, plural, one {ce modèle} many {ces modèles} other {ces modèles}} :", "xpack.idxMgmt.deleteTemplatesModal.modalTitleText": "Supprimer {numTemplatesToDelete, plural, one {modèle} many {# modèles} other {# modèles}}", @@ -18676,9 +18629,7 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryHelpText": "Valeur par défaut : requête {code}.", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type} correspond à un nombre, une date ou une plage d'adresses IP.", "xpack.idxMgmt.flushIndicesAction.indexFlushedMessage": "L'index {indexNames} a été vidé.", - "xpack.idxMgmt.flushIndicesAction.successfullyFlushedIndicesMessage": "Vidage effectué avec succès : [{indexNames}]", "xpack.idxMgmt.forceMergeIndicesAction.indexForcemergedMessage": "L'index {indexNames} a fait l'objet d'une fusion forcée.", - "xpack.idxMgmt.forceMergeIndicesAction.successfullyForceMergedIndicesMessage": "Fusion forcée effectué avec succès : [{indexNames}]", "xpack.idxMgmt.formWizard.stepAliases.aliasesEditorHelpText": "Utiliser le format JSON : {code}", "xpack.idxMgmt.formWizard.stepSettings.settingsEditorHelpText": "Utiliser le format JSON : {code}", "xpack.idxMgmt.goToDiscover.showIndexToolTip": "Afficher {indexName} dans Discover", @@ -18775,9 +18726,7 @@ "xpack.idxMgmt.mappingsEditor.sourceFieldDescription": "Le champ _source contient le corps du document JSON d'origine qui a été fourni au moment de l'indexation. Vous pouvez nettoyer des champs individuels en définissant ceux à inclure ou exclure du champ _source. {docsLink}", "xpack.idxMgmt.mappingsEditor.typeField.documentationLinkLabel": "Documentation de {typeName}", "xpack.idxMgmt.openIndicesAction.indexOpenedMessage": "L'index {indexNames} a été ouvert.", - "xpack.idxMgmt.openIndicesAction.successfullyOpenedIndicesMessage": "Ouverture réussie : [{indexNames}]", "xpack.idxMgmt.refreshIndicesAction.indexRefreshedMessage": "L'index {indexNames} a été actualisé.", - "xpack.idxMgmt.refreshIndicesAction.successfullyRefreshedIndicesMessage": "Actualisation réussie : [{indexNames}]", "xpack.idxMgmt.templateDetails.summaryTab.indexPatternsDescriptionListTitle": "{numIndexPatterns, plural, one {Modèle} many {Modèles d''indexation manquants} other {Modèles}} d'index", "xpack.idxMgmt.templateForm.stepLogistics.dataStreamDescription": "Le modèle crée des flux de données au lieu d'index. {docsLink}", "xpack.idxMgmt.templateForm.stepLogistics.fieldIndexPatternsHelpText": "Les espaces et les caractères {invalidCharactersList} ne sont pas autorisés.", @@ -20345,62 +20294,58 @@ "xpack.infra.appName": "Logs Infra", "xpack.infra.assetDetails.alerts.tooltip.documentationLink": "documentation", "xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "Afficher tout", - "xpack.infra.assetDetails.formulas.cpuUsage": "Utilisation CPU", - "xpack.infra.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", - "xpack.infra.assetDetails.formulas.cpuUsage.irqLabel": "irq", - "xpack.infra.assetDetails.formulas.cpuUsage.niceLabel": "nice", - "xpack.infra.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", - "xpack.infra.assetDetails.formulas.cpuUsage.stealLabel": "steal", - "xpack.infra.assetDetails.formulas.cpuUsage.systemLabel": "system", - "xpack.infra.assetDetails.formulas.cpuUsage.userLabel": "utilisateur", - "xpack.infra.assetDetails.formulas.diskIORead": "Entrées et sorties par seconde en lecture sur le disque", - "xpack.infra.assetDetails.formulas.diskIOWrite": "Entrées et sorties par seconde en écriture sur le disque", - "xpack.infra.assetDetails.formulas.diskReadThroughput": "Rendement de lecture du disque", - "xpack.infra.assetDetails.formulas.diskSpaceAvailability": "Disponibilité de l'espace disque", - "xpack.infra.assetDetails.formulas.diskSpaceAvailable": "Espace disque disponible", - "xpack.infra.assetDetails.formulas.diskUsage": "Utilisation du disque", - "xpack.infra.assetDetails.formulas.diskWriteThroughput": "Rendement d’écriture du disque", - "xpack.infra.assetDetails.formulas.hostCount.hostsLabel": "Hôtes", - "xpack.infra.assetDetails.formulas.kubernetes.capacity": "Capacité", - "xpack.infra.assetDetails.formulas.kubernetes.used": "Utilisé", - "xpack.infra.assetDetails.formulas.load15m": "Charge (15 min)", - "xpack.infra.assetDetails.formulas.load1m": "Charge (1 min)", - "xpack.infra.assetDetails.formulas.load5m": "Charge (5 min)", - "xpack.infra.assetDetails.formulas.logRate": "Taux de log", - "xpack.infra.assetDetails.formulas.memoryFree": "Sans mémoire", - "xpack.infra.assetDetails.formulas.memoryUsage": "Utilisation mémoire", - "xpack.infra.assetDetails.formulas.metric.label.cache": "cache", - "xpack.infra.assetDetails.formulas.metric.label.free": "gratuit", - "xpack.infra.assetDetails.formulas.metric.label.used": "utilisé", - "xpack.infra.assetDetails.formulas.normalizedLoad1m": "Charge normalisée", - "xpack.infra.assetDetails.formulas.rx": "Réseau entrant (RX)", - "xpack.infra.assetDetails.formulas.tx": "Réseau sortant (TX)", + "xpack.metricsData.assetDetails.formulas.cpuUsage": "Utilisation CPU", + "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", + "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", + "xpack.metricsData.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", + "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "system", + "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "utilisateur", + "xpack.metricsData.assetDetails.formulas.diskIORead": "Entrées et sorties par seconde en lecture sur le disque", + "xpack.metricsData.assetDetails.formulas.diskIOWrite": "Entrées et sorties par seconde en écriture sur le disque", + "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "Rendement de lecture du disque", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "Disponibilité de l'espace disque", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "Espace disque disponible", + "xpack.metricsData.assetDetails.formulas.diskUsage": "Utilisation du disque", + "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "Rendement d’écriture du disque", + "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "Hôtes", + "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "Capacité", + "xpack.metricsData.assetDetails.formulas.kubernetes.used": "Utilisé", + "xpack.metricsData.assetDetails.formulas.load15m": "Charge (15 min)", + "xpack.metricsData.assetDetails.formulas.load1m": "Charge (1 min)", + "xpack.metricsData.assetDetails.formulas.load5m": "Charge (5 min)", + "xpack.metricsData.assetDetails.formulas.logRate": "Taux de log", + "xpack.metricsData.assetDetails.formulas.memoryFree": "Sans mémoire", + "xpack.metricsData.assetDetails.formulas.memoryUsage": "Utilisation mémoire", + "xpack.metricsData.assetDetails.formulas.metric.label.cache": "cache", + "xpack.metricsData.assetDetails.formulas.metric.label.free": "gratuit", + "xpack.metricsData.assetDetails.formulas.metric.label.used": "utilisé", + "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "Charge normalisée", + "xpack.metricsData.assetDetails.formulas.rx": "Réseau entrant (RX)", + "xpack.metricsData.assetDetails.formulas.tx": "Réseau sortant (TX)", + "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "Entrées et sorties par seconde (IOPS) sur le disque", + "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "Rendement du disque", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.available": "Disponible", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "Utilisé", + "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "Utilisation du disque par point de montage", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "Capacité CPU du nœud", + "xpack.metricsData.assetDetails.metricsCharts.load": "Charge", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "Cache", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "Gratuit", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "Lire", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "Utilisé", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "Écrire", + "xpack.metricsData.assetDetails.metricsCharts.network": "Réseau", + "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "Entrant (RX)", + "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "Sortant (TX)", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "Capacité du disque du nœud", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "Capacité de mémoire du nœud", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "Capacité de pod du nœud", + "xpack.metricsData.assetDetails.overview.kpi.subtitle.average": "Moyenne", "xpack.infra.assetDetails.header.return": "Renvoyer", "xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name", "xpack.infra.assetDetails.metadata.tooltip.metadata": "Métadonnées", - "xpack.infra.assetDetails.metricsCharts.cpuUsage": "Utilisation CPU", - "xpack.infra.assetDetails.metricsCharts.diskIOPS": "Entrées et sorties par seconde (IOPS) sur le disque", - "xpack.infra.assetDetails.metricsCharts.diskThroughput": "Rendement du disque", - "xpack.infra.assetDetails.metricsCharts.diskUsage": "Utilisation du disque", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.available": "Disponible", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.used": "Utilisé", - "xpack.infra.assetDetails.metricsCharts.diskUsageByMountingPoint": "Utilisation du disque par point de montage", - "xpack.infra.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "Capacité CPU du nœud", - "xpack.infra.assetDetails.metricsCharts.load": "Charge", - "xpack.infra.assetDetails.metricsCharts.logRate": "Taux de log", - "xpack.infra.assetDetails.metricsCharts.memoryUsage": "Utilisation mémoire", - "xpack.infra.assetDetails.metricsCharts.metric.label.cache": "Cache", - "xpack.infra.assetDetails.metricsCharts.metric.label.free": "Gratuit", - "xpack.infra.assetDetails.metricsCharts.metric.label.read": "Lire", - "xpack.infra.assetDetails.metricsCharts.metric.label.used": "Utilisé", - "xpack.infra.assetDetails.metricsCharts.metric.label.write": "Écrire", - "xpack.infra.assetDetails.metricsCharts.network": "Réseau", - "xpack.infra.assetDetails.metricsCharts.network.label.rx": "Entrant (RX)", - "xpack.infra.assetDetails.metricsCharts.network.label.tx": "Sortant (TX)", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeDiskCapacity": "Capacité du disque du nœud", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeMemoryCapacity": "Capacité de mémoire du nœud", - "xpack.infra.assetDetails.metricsCharts.nginx.nodePodCapacity": "Capacité de pod du nœud", - "xpack.infra.assetDetails.metricsCharts.normalizedLoad1m": "Charge normalisée", "xpack.infra.assetDetails.overview.alertsSectionTitle": "Alertes", "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Aperçu Kubernetes", "xpack.infra.assetDetails.overview.metadataSectionTitle": "Métadonnées", @@ -20413,11 +20358,6 @@ "xpack.infra.assetDetailsEmbeddable.displayName": "Détails de ressource", "xpack.infra.assetDetailsEmbeddable.metadataSummary.showAllMetadataButton": "Afficher tout", "xpack.infra.assetDetailsEmbeddable.notApplicableLabel": "S. O.", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.cpuUsage.title": "Utilisation CPU", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.diskUsage.title": "Utilisation du disque", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.memoryUsage.title": "Utilisation mémoire", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.normalizedLoad1m.title": "Charge normalisée", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.subtitle.average": "Moyenne", "xpack.infra.assetDetailsEmbeddable.overview.metadataCloudProviderHeading": "Fournisseur cloud", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostIpHeading": "IP de l'hôte", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostOsVersionHeading": "Version de l'OS de l'hôte", @@ -20535,19 +20475,7 @@ "xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "Rechercher les entrées de logs...", "xpack.infra.hostsViewPage.tabs.logs.title": "Logs", "xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "Ouvrir dans Lens", - "xpack.infra.hostsViewPage.tabs.metricsCharts.cpuUsage": "Utilisation CPU", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIORead": "Entrées et sorties par seconde en lecture sur le disque", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIOWrite": "Entrées et sorties par seconde en écriture sur le disque", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskReadThroughput": "Rendement de lecture du disque", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceAvailable": "Espace disque disponible", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskUsage": "Utilisation du disque", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskWriteThroughput": "Rendement d’écriture du disque", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryFree": "Sans mémoire", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryUsage": "Utilisation mémoire", - "xpack.infra.hostsViewPage.tabs.metricsCharts.normalizedLoad1m": "Charge normalisée", - "xpack.infra.hostsViewPage.tabs.metricsCharts.rx": "Réseau entrant (RX)", "xpack.infra.hostsViewPage.tabs.metricsCharts.title": "Indicateurs", - "xpack.infra.hostsViewPage.tabs.metricsCharts.tx": "Réseau sortant (TX)", "xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "Que sont ces indicateurs ?", "xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "Pourquoi des lignes pointillées apparaissent-elles ?", "xpack.infra.infra.assetDetails.alerts.createAlertLink": "Créer une règle", @@ -22349,7 +22277,6 @@ "xpack.lens.config.configFlyoutCallout": "ES|QL propose actuellement des options de configuration limitées", "xpack.lens.config.editLabel": "Modifier la configuration", "xpack.lens.config.editLinkLabel": "Modifier dans Lens", - "xpack.lens.config.editVisualizationLabel": "Modifier la visualisation", "xpack.lens.config.experimentalLabel": "Version d'évaluation technique", "xpack.lens.configPanel.addLayerButton": "Ajouter un calque", "xpack.lens.configPanel.experimentalLabel": "Version d'évaluation technique", @@ -24361,7 +24288,6 @@ "xpack.ml.dataframe.analytics.create.analyticsProgressErrorMessage": "Une erreur s'est produite lors de la récupération des statistiques de progression pour la tâche d'analyse {jobId}", "xpack.ml.dataframe.analytics.create.configDetails.includedFieldsAndMoreDescription": "{includedFields} ... (et {extraCount} de plus)", "xpack.ml.dataframe.analytics.create.createDataViewSuccessMessage": "La vue de donnée Kibana {dataViewName} a été créée.", - "xpack.ml.dataframe.analytics.create.dataViewExistsError": "Une vue de données avec le titre {title} existe déjà.", "xpack.ml.dataframe.analytics.create.dependentVariableMaxDistictValuesError": "Non valide. {message}", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.description": "Fournissez les liens des résultats des analyses aux tableaux de bord Kibana, à Discover ou à d'autres pages Web. {learnMoreLink}", "xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessageError": "La vue de données {dataViewName} existe déjà.", @@ -25239,8 +25165,6 @@ "xpack.ml.dataframe.analytics.create.configDetails.standardizationEnabled": "Normalisation activée", "xpack.ml.dataframe.analytics.create.configDetails.trainingPercent": "Pourcentage d'entraînement", "xpack.ml.dataframe.analytics.create.createDataViewErrorMessage": "Une erreur est survenue lors de la création de la vue de données Kibana :", - "xpack.ml.dataframe.analytics.create.createDataViewLabel": "Créer une vue de données", - "xpack.ml.dataframe.analytics.create.dataViewPermissionWarning": "Vous devez disposer d'une autorisation pour pouvoir créer des vues de données.", "xpack.ml.dataframe.analytics.create.dependentVariableClassificationPlaceholder": "Sélectionnez le champ numérique, de catégorie ou booléen sur lequel vous souhaitez effectuer une prédiction.", "xpack.ml.dataframe.analytics.create.dependentVariableInputAriaLabel": "Entrez le champ à utiliser comme variable dépendante.", "xpack.ml.dataframe.analytics.create.dependentVariableLabel": "Variable dépendante", @@ -25248,18 +25172,11 @@ "xpack.ml.dataframe.analytics.create.dependentVariableOptionsNoNumericalFields": "Aucun champ numérique n'a été trouvé pour cette vue de données.", "xpack.ml.dataframe.analytics.create.dependentVariableRegressionPlaceholder": "Sélectionnez le champ numérique sur lequel vous souhaitez effectuer une prédiction.", "xpack.ml.dataframe.analytics.create.destinationIndexHelpText": "Un index portant ce nom existe déjà. Notez que l'exécution de cette tâche d'analyse modifiera cet index de destination.", - "xpack.ml.dataframe.analytics.create.destinationIndexInputAriaLabel": "Choisissez un nom d'index de destination unique.", - "xpack.ml.dataframe.analytics.create.destinationIndexInvalidError": "Nom d'index de destination non valide.", - "xpack.ml.dataframe.analytics.create.destinationIndexLabel": "Index de destination", "xpack.ml.dataframe.analytics.create.destinationIndexNotCreatedForDataFrameAnalyticsJob": "Aucun index de destination n’a encore été créé.", - "xpack.ml.dataframe.analytics.create.DestIndexSameAsIdLabel": "Index de destination identique à l'ID de tâche", "xpack.ml.dataframe.analytics.create.detailsDetails.editButtonText": "Modifier", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrls.title": "URL personnalisées", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.learnMoreLinkText": "En savoir plus", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSectionButton": "Paramètres supplémentaires", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldHelpText": "Sélectionnez un champ temporel principal à utiliser avec le filtre temporel global.", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldLabel": "Champ temporel pour la vue de données Kibana", - "xpack.ml.dataframe.analytics.create.detailsStep.noTimeFieldOptionLabel": "Je ne souhaite pas utiliser l'option de champ temporel", "xpack.ml.dataframe.analytics.create.downsampleFactorInputAriaLabel": "Fraction de données utilisée pour calculer les dérivés de la fonction de perte pour l'entraînement de l'arborescence.", "xpack.ml.dataframe.analytics.create.downsampleFactorLabel": "Facteur de sous-échantillonnage", "xpack.ml.dataframe.analytics.create.downsampleFactorText": "Fraction de données utilisée pour calculer les dérivés de la fonction de perte pour l'entraînement de l'arborescence. Doit être compris entre 0 et 1.", @@ -25346,9 +25263,8 @@ "xpack.ml.dataFrame.analytics.create.searchSelection.CcsErrorCallOutTitle": "Les vues de données utilisant la recherche inter-clusters ne sont pas prises en charge.", "xpack.ml.dataFrame.analytics.create.searchSelection.errorGettingDataViewTitle": "Erreur lors du chargement de la vue de données utilisée par la recherche enregistrée", "xpack.ml.dataFrame.analytics.create.searchSelection.notFoundLabel": "Aucun recherche enregistrée ni aucun index correspondants n'ont été trouvés.", - "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.indexPattern": "Vue de données", + "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.dataView": "Vue de données", "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.search": "Recherche enregistrée", - "xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage": "Vous ne pourrez peut-être pas visualiser les résultats de la tâche si aucune vue de données n'est créée pour l'index de destination.", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitInputAriaLabel": "Les arbres de décision dépassant cette profondeur sont pénalisés dans les calculs de perte.", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitLabel": "Limite de profondeur d'arborescence non stricte", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitText": "Les arbres de décision dépassant cette profondeur sont pénalisés dans les calculs de perte. Doit être supérieur ou égal à 0. ", @@ -25563,7 +25479,6 @@ "xpack.ml.dataframe.analyticsMap.noJobSelectedLabel": "Aucun ID d'analyse sélectionné", "xpack.ml.dataframe.analyticsMap.title": "Mapping pour l’analyse", "xpack.ml.dataframe.analyticsSourceSelection.title": "Nouvelle tâche d'analyse / Sélectionner une vue de données source", - "xpack.ml.dataframe.stepDetailsForm.destinationIndexInvalidErrorLink": "Découvrez les limitations relatives aux noms d'index.", "xpack.ml.dataFrameAnalytics.analyticsMap.docTitle": "Mapping d'analyse", "xpack.ml.dataFrameAnalytics.createJob.docTitle": "Créer une tâche", "xpack.ml.dataFrameAnalytics.exploration.docTitle": "Explorateur de résultats", @@ -29235,7 +29150,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.customEquationTooltip": "Ceci est compatible avec des calculs de base (A + B / C) et la logique booléenne (A < B ? A : B).", "xpack.observability.customThreshold.rule.alertFlyout.dataViewError.noTimestamp": "La vue de données sélectionnée ne dispose pas de champ d'horodatage. Veuillez sélectionner une autre vue de données.", "xpack.observability.customThreshold.rule.alertFlyout.defineTextQueryPrompt": "Définir le filtre de recherche (facultatif)", - "xpack.observability.customThreshold.rule.alertFlyout.error.aggregationRequired": "L'agrégation est requise.", "xpack.observability.customThreshold.rule.alertFlyout.error.equation.invalidCharacters": "Le champ d'équation prend en charge uniquement les caractères suivants : A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, =", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "La requête de filtre n'est pas valide.", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "La vue de données est requise.", @@ -32359,12 +32273,7 @@ "xpack.securitySolution.responseActionsList.list.recordRange": "Affichage de {range} sur {total} {recordsLabel}", "xpack.securitySolution.responseActionsList.list.recordRangeLabel": "{records, plural, one {action de réponse} many {actions de réponse} other {actions de réponse}}", "xpack.securitySolution.riskInformation.howCalculatedText": "Le dispositif Entity Risk s'exécute toutes les heures afin de regrouper les alertes \"Open\" et \"Acknowledged\" des 30 derniers jours et attribue un score de risque à l'hôte ou à l'utilisateur. Il agrège ensuite les scores de risque individuels et les normalise dans une fourchette de 0 à 100 à l'aide de {riemannZetaLink} .", - "xpack.securitySolution.riskInformation.learnMore": "En savoir plus sur les risques {riskEntity}.", "xpack.securitySolution.riskInformation.riskHeader": "Plage de scores de risque de {riskEntity}", - "xpack.securitySolution.riskInformation.riskScoreFieldLabel": "Score de risque de {riskEntity}", - "xpack.securitySolution.riskInformation.riskScoreFieldText": "Le champ {riskScoreField} représente le risque normalisé du {riskEntity} sous la forme d'une valeur numérique unique. Vous pouvez utiliser cette valeur comme indicateur relatif du risque dans les playbooks de triage et de réponse.", - "xpack.securitySolution.riskInformation.riskScoreLevelLabel": "Niveau de risque {riskEntity}", - "xpack.securitySolution.riskInformation.riskScoreLevelText": "Le champ {riskLevelField} représente l'un des six niveaux de risque de {riskEntity}, sur la base d'indicateurs de risque prédéfinis.", "xpack.securitySolution.riskScore.api.ingestPipeline.delete.errorMessageTitle": "Impossible de supprimer {totalCount, plural, =1 {pipeline} one {pipelines} many {pipelines} other {pipelines}} d'ingestion", "xpack.securitySolution.riskScore.api.transforms.delete.errorMessageTitle": "Impossible de supprimer {totalCount, plural, =1 {Transformer} one {Transformations} many {Transformations} other {Transformations}}", "xpack.securitySolution.riskScore.api.transforms.start.errorMessageTitle": "Impossible de démarrer {totalCount, plural, =1 {Transformer} one {Transformations} many {Transformations} other {Transformations}}", @@ -36718,7 +36627,7 @@ "xpack.securitySolution.timeline.saveTimeline.modal.header": "Enregistrer la chronologie", "xpack.securitySolution.timeline.saveTimeline.modal.optionalLabel": "Facultatif", "xpack.securitySolution.timeline.saveTimeline.modal.titleAriaLabel": "Titre", - "xpack.securitySolution.timeline.saveTimeline.modal.titleTitle": "Titre", + "xpack.securitySolution.timeline.saveTimeline.modal.title": "Titre", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.discard.title": "Abandonner le modèle de chronologie", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.header": "Enregistrer le modèle de chronologie", "xpack.securitySolution.timeline.searchOrFilter.filterDescription": "Les événements des fournisseurs de données ci-dessus sont filtrés par le KQL adjacent", @@ -40587,7 +40496,6 @@ "xpack.transform.stepCreateForm.createAlertRuleDescription": "Ouvre un assistant pour créer une règle d'alerte permettant de monitorer l'intégrité de la transformation.", "xpack.transform.stepCreateForm.createAndStartTransformButton": "Créer et démarrer", "xpack.transform.stepCreateForm.createAndStartTransformDescription": "Crée et démarre la transformation. Une transformation permet d'augmenter la charge de recherche et d'indexation dans votre cluster. Néanmoins, arrêtez la transformation en cas de charge excessive. Une fois la transformation commencée, vous pourrez choisir entre différentes options pour continuer à l’explorer.", - "xpack.transform.stepCreateForm.createDataViewLabel": "Créer une vue de données Kibana", "xpack.transform.stepCreateForm.createTransformButton": "Créer", "xpack.transform.stepCreateForm.createTransformDescription": "Crée la transformation sans la démarrer. Vous pourrez démarrer la transformation ultérieurement en revenant à la liste de transformations.", "xpack.transform.stepCreateForm.creatingDataViewMessage": "Création de la vue de données Kibana...", @@ -40673,15 +40581,7 @@ "xpack.transform.stepDetailsForm.continuousModeDelayLabel": "Retard", "xpack.transform.stepDetailsForm.continuousModeError": "Le mode continu n'est pas disponible pour les index sans champ de date.", "xpack.transform.stepDetailsForm.createIndexAPI": "API de création d'index", - "xpack.transform.stepDetailsForm.dataViewPermissionWarning": "Vous devez disposer d'une autorisation pour pouvoir créer des vues de données.", - "xpack.transform.stepDetailsForm.dataViewTimeFieldHelpText": "Sélectionnez un champ temporel principal à utiliser avec le filtre temporel global.", - "xpack.transform.stepDetailsForm.dataViewTimeFieldLabel": "Champ temporel pour la vue de données Kibana", - "xpack.transform.stepDetailsForm.dataViewTitleError": "Une vue de données avec ce titre existe déjà.", "xpack.transform.stepDetailsForm.destinationIndexHelpText": "Un index portant ce nom existe déjà. Notez que l'exécution de cette transformation modifiera cet index de destination.", - "xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel": "Choisissez un nom d'index de destination unique.", - "xpack.transform.stepDetailsForm.destinationIndexInvalidError": "Nom d'index de destination non valide.", - "xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink": "Découvrez les limitations relatives aux noms d'index.", - "xpack.transform.stepDetailsForm.destinationIndexLabel": "Index de destination", "xpack.transform.stepDetailsForm.destinationIngestPipelineAriaLabel": "Sélectionner un pipeline d'ingestion (facultatif)", "xpack.transform.stepDetailsForm.destinationIngestPipelineComboBoxPlaceholder": "Sélectionner un pipeline d'ingestion (facultatif)", "xpack.transform.stepDetailsForm.destinationIngestPipelineLabel": "Pipeline d'ingestion de destination", @@ -40700,7 +40600,6 @@ "xpack.transform.stepDetailsForm.maxPageSearchSizeHelpText": "La taille de page initiale à utiliser pour l'agrégation imbriquée de chaque point de contrôle.", "xpack.transform.stepDetailsForm.maxPageSearchSizeLabel": "Taille maximale de recherche de pages", "xpack.transform.stepDetailsForm.missingBucketCheckboxHelpTextLink": "En savoir plus", - "xpack.transform.stepDetailsForm.noTimeFieldOptionLabel": "Je ne souhaite pas utiliser l'option de champ temporel", "xpack.transform.stepDetailsForm.numFailureRetriesAriaLabel": "Choisissez un nombre maximum de tentatives.", "xpack.transform.stepDetailsForm.NumFailureRetriesError": "Le nombre de tentatives doit être compris entre 0 et 100, ou égal à -1 pour des tentatives infinies.", "xpack.transform.stepDetailsForm.retentionPolicyDateFieldHelpText": "Sélectionnez le champ de date pouvant être utilisé pour identifier les documents obsolètes dans l'index de destination.", @@ -43043,7 +42942,6 @@ "cloud.deploymentDetails.cloudIDLabel": "Identifiant du cloud", "cloud.deploymentDetails.createManageApiKeysButtonLabel": "Créer et gérer des clés d'API", "cloud.deploymentDetails.elasticEndpointLabel": "Point de terminaison Elastic", - "cloud.deploymentDetails.helpMenuLinks.endpoints": "Points de terminaison", "cloud.deploymentDetails.learnMoreButtonLabel": "En savoir plus", "cloud.deploymentDetails.modal.closeButtonLabel": "Fermer", "cloud.deploymentDetails.modal.learnMoreButtonLabel": "En savoir plus", @@ -43481,7 +43379,6 @@ "unifiedDocViewer.sourceViewer.errorMessageTitle": "Une erreur s'est produite.", "unifiedDocViewer.sourceViewer.refresh": "Actualiser", "utils.filename.pathWarning": "Le chemin est peut-être incorrectement formé ; vérifiez la valeur", - "utils.filename.wildcardWarning": "l'utilisation de caractères génériques dans les chemins de fichier peut affecter les performances du point de terminaison", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "Active la bibliothèque de graphiques héritée pour les graphiques de jauge dans Visualize.", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "Bibliothèque de graphiques héritée pour les jauges", "visTypeGauge.controls.gaugeOptions.alignmentLabel": "Alignement", @@ -43634,7 +43531,6 @@ "xpack.cloudDataMigration.upgrade.text": "Effectuer la mise à niveau vers les versions plus récentes beaucoup plus facilement", "xpack.cloudLinks.deploymentLinkLabel": "Gérer ce déploiement", "xpack.cloudLinks.helpMenuLinks.documentation": "Documentation", - "xpack.cloudLinks.helpMenuLinks.endpoints": "Points de terminaison", "xpack.cloudLinks.helpMenuLinks.giveFeedback": "Donner un retour", "xpack.cloudLinks.helpMenuLinks.support": "Support technique", "xpack.cloudLinks.setupGuide": "Guides de configuration", @@ -43750,4 +43646,4 @@ "xpack.serverlessObservability.nav.projectSettings": "Paramètres de projet", "xpack.serverlessObservability.nav.visualizations": "Visualisations" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a8e3d4b417f54..5357b3a0908ad 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2065,7 +2065,6 @@ "data.search.esErrorTitle": "検索結果を取得できません", "data.search.esql.help": "ES|QLを使用してElasticsearchを照会します。", "data.search.esql.query.help": "ES|QLクエリ。", - "data.search.esql.timezone.help": "日付操作の際に使用するタイムゾーンです。有効なISO8601フォーマットとUTCオフセットの両方が機能します。", "data.search.essql.count.help": "取得するドキュメント数です。パフォーマンスを向上させるには、小さなデータセットを使用します。", "data.search.essql.help": "Elasticsearch SQLを使用してElasticsearchを照会します。", "data.search.essql.locale.help": "使用するロケール。", @@ -5323,7 +5322,6 @@ "savedSearch.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。", "savedSearch.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します", "searchApiPanels.welcomeBanner.header.greeting.customTitle": "{name}様", - "searchApiPanels.welcomeBanner.ingestData.clientDocLink": "{languageName}APIリファレンス", "searchApiPanels.welcomeBanner.installClient.clientDocLink": "{languageName}クライアントドキュメント", "searchApiPanels.welcomeBanner.selectClient.description": "Elasticは複数の一般的な言語でクライアントを構築および保守します。Elasticのコミュニティはさらに多くを提供しています。お気に入りの言語クライアントを選択するか、{console}を起動して開始します。", "searchApiPanels.welcomeBanner.codeBox.copyButtonLabel": "コピー", @@ -5331,22 +5329,7 @@ "searchApiPanels.welcomeBanner.header.description": "プログラミング言語のクライアントを設定し、データを取り込めば、数分で検索を開始できます。", "searchApiPanels.welcomeBanner.header.greeting.defaultTitle": "こんにちは。", "searchApiPanels.welcomeBanner.header.title": "Elasticsearchをはじめよう", - "searchApiPanels.welcomeBanner.ingestData.beatsDescription": "Elasticsearch向けの軽量の、専用データ転送機能。Beatsを使用して、サーバーから運用データを送信します。", - "searchApiPanels.welcomeBanner.ingestData.beatsLink": "ビート", - "searchApiPanels.welcomeBanner.ingestData.beatsTitle": "ビート", - "searchApiPanels.welcomeBanner.ingestData.connectorsDescription": "サードパーティのソースからElasticsearchにデータを同期するための特別な統合。Elasticコネクターを使って、さまざまなデータベースやオブジェクトストアからコンテンツを同期できます。", - "searchApiPanels.welcomeBanner.ingestData.connectorsPythonLink": "connectors-python", - "searchApiPanels.welcomeBanner.ingestData.connectorsTitle": "コネクタークライアント", "searchApiPanels.welcomeBanner.ingestData.description": "データストリームやインデックスにデータを追加して、データを検索可能にします。アプリケーションとワークフローに合ったインジェスト方法を選択します。", - "searchApiPanels.welcomeBanner.ingestData.ingestApiDescription": "データをインデックス化する最も柔軟な方法で、カスタマイズや最適化オプションを完全に制御できます。", - "searchApiPanels.welcomeBanner.ingestData.ingestApiLabel": "API経由でインジェスト", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationDescription": "データを変換してElasticsearchに送信するために最適化された専用のインジェストツール。", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationLabel": "統合経由でインジェスト", - "searchApiPanels.welcomeBanner.ingestData.ingestLegendLabel": "インジェスチョン方法を選択", - "searchApiPanels.welcomeBanner.ingestData.integrationsLink": "統合について", - "searchApiPanels.welcomeBanner.ingestData.logstashDescription": "データストリームやインデックスにデータを追加して、データを検索可能にします。アプリケーションとワークフローに合ったインジェスト方法を選択します。", - "searchApiPanels.welcomeBanner.ingestData.logstashLink": "Logstash", - "searchApiPanels.welcomeBanner.ingestData.logstashTitle": "Logstash", "searchApiPanels.welcomeBanner.ingestData.title": "データをインジェスト", "searchApiPanels.welcomeBanner.installClient.description": "Elasticは複数の一般的な言語でクライアントを構築および保守します。Elasticのコミュニティはさらに多くを提供しています。開始するには、お気に入りの言語クライアントをインストールします。", "searchApiPanels.welcomeBanner.installClient.title": "クライアントをインスト-ル", @@ -12871,7 +12854,6 @@ "xpack.dataVisualizer.table.expandRowScreenMsg": "行を展開", "xpack.dataVisualizer.title": "ファイルをアップロード", "xpack.elasticAssistant.assistant.connectors.connectorMissingCallout.calloutDescription": "上または{link}からコネクターを選択して続行します。", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactText": "興味深い事実:Kibanaサーバーのログで進行状況を確認し、完了したら{funFacts}でDiscoverに結果を表示します。データセットによっては(何分も)かかります。このダイアログを閉じると評価はキャンセルされます!", "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "始めるには、{machineLearning}でELSERを設定してください。{seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "`{kbIndexPattern}`に初期化しました", "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "AIシステムからの応答は、必ずしも完全に正確であるとは限りません。アシスタント機能とその使用方法の詳細については、{documentationLink}を参照してください。", @@ -12999,7 +12981,6 @@ "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationPromptLabel": "評価プロンプト", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeDescription": "実行する評価のタイプ(例:\"correctness\" \"esql-validator\"、または\"custom\")。独自の評価プロンプトを指定します", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeLabel": "評価タイプ", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetDescription": "評価するサンプルデータセット。\"input\"プロパティと\"references\"プロパティを含むオブジェクトの配列", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetLabel": "データセット", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactDiscoverLinkText": "ここをクリック", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorModelDescription": "で最終評価を実行するモデル", @@ -13162,7 +13143,6 @@ "xpack.enterpriseSearch.content.index.connector.syncRules.flyout.errorTitle": "同期{idsLength, plural, other {ルール}}{ids}{idsLength, plural, other {あります}}無効です。", "xpack.enterpriseSearch.content.index.pipelines.copyCustomizeCallout.description": "インデックスはデフォルトインジェストパイプライン\"{defaultPipeline}\"を使用しています。パイプラインをインデックス固有の構成にコピーし、カスタムインジェストと推論パイプラインを作成できるようにします。", "xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyAPIText": "{apiIndex}以下の設定に行われた変更は参照専用です。これらの設定は、インデックスまたはパイプラインまで永続しません。", - "xpack.enterpriseSearch.content.indices.callout.text": "Elasticsearchインデックスは、現在、Searchの中心です。直接そのインデックスを使用して、新しいインデックスを作成し、検索エクスペリエンスを構築できます。SearchでのElasticsearchの使用方法の詳細については、{docLink}をご覧ください", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.description": "まず、Elasticsearch APIキーを生成します。この{apiKeyName}は、コネクターがドキュメントを作成された{indexName}インデックスにインデックスするための読み書き権限を有効にします。キーは安全な場所に保管してください。コネクターを構成するときに必要になります。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected": "コネクター{name}は、正常にSearchに接続されました。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.secondParagraph": "コネクターリポジトリには複数の{link}が含まれています。当社のフレームワークを使用すると、カスタムデータソース用のコネクターの開発を加速できます。", @@ -14378,8 +14358,6 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.done": "完了", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.generateButton": "キーを生成", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "分析APIキーを作成", - "xpack.enterpriseSearch.content.callout.dismissButton": "閉じる", - "xpack.enterpriseSearch.content.callout.title": "SearchでのElasticsearchインデックスの概要", "xpack.enterpriseSearch.content.cannotConnect.body": "詳細。", "xpack.enterpriseSearch.content.cannotConnect.title": "エンタープライズ サーチに接続できません", "xpack.enterpriseSearch.content.crawler.authentication": "認証", @@ -14481,7 +14459,6 @@ "xpack.enterpriseSearch.content.index.syncButton.label": "同期", "xpack.enterpriseSearch.content.index.syncButton.syncing.label": "同期中", "xpack.enterpriseSearch.content.index.syncButton.waitingForSync.label": "同期を待機しています", - "xpack.enterpriseSearch.content.indices.callout.docLink": "ドキュメントを読む", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.button.label": "APIキーを生成", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.cancelButton.label": "キャンセル", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.confirmButton.label": "APIキーを生成", @@ -14615,16 +14592,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.createErrors": "パイプラインの作成エラー", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.esDocs.link": "学習されたモデルの追加方法の詳細", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.imageAlt": "機械学習モデル例がありません", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.chooseExistingLabel": "新規または既存", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.description": "メインパイプラインでプロセッサーとして使用される子パイプラインを作成または再利用します。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionDeployTrainedModel": "クラスターで自然言語処理タスクを実行するには、適切な学習済みモデルをデプロイする必要があります。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionUsePipelines": "作成するパイプラインはElasticデプロイの場所で使用されるために保存されます。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink": "SearchでのMLモデルのインポートと使用の詳細", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.emptyValueError": "フィールドが必要です。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.chooseLabel": "選択", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.existingLabel": "既存のパイプライン", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.model": "モデル", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.newLabel": "新しいパイプライン", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.placeholder": "1 つ選択してください", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.sourceFields": "ソースフィールド", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipelineLabel": "既存の推論パイプラインを選択", @@ -14634,11 +14604,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName": "名前には文字、数字、アンダースコア、ハイフンのみ使用する必要があります。", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.placeholder": "モデルを選択", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.redactedValue": "このモデルはKibanaスペースで使用できません", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.modelLabel": "学習済みMLモデルを選択", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.nameLabel": "名前", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.namePlaceholder": "このパイプラインの一意の名前を入力", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.pipelineNameExistsError": "名前はすでに別のパイプラインで使用されています。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.title": "パイプラインを作成または入力", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.titleSelectTrainedModel": "学習済みMLモデルを選択", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions": "アクション", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions.deleteMapping": "このマッピングを削除", @@ -14675,15 +14643,6 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.subtitle.result": "結果", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.title": "パイプライン結果をテスト", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.useJsonFormat": "このJSON形式を使用して、独自のドキュメントの配列を追加します", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.description": "パイプラインを通してドキュメントのインデックス作成を開始する前に、インデックスマッピングを手動で更新する必要があります。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.descriptionNoAction": "インデックスマッピングは、選択した推論出力フィールドを含むように自動的に更新されます。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.docsLink": "詳細", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappings": "フィールドマッピング", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappingsRequired": "必要なフィールドマッピング", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.presentInMapping": "選択した推論出力フィールドがマッピングに存在することを確認します。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.required": "必須", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.title": "インデックスマッピングを更新", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.titleNoAction": "インデックスマッピングの更新をレビュー", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.title": "推論パイプラインの追加", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.apiIndexSubtitle": "インジェストパイプラインは、検索アプリケーションのインデックスを最適化します。APIベースのインデックスでこれらのパイプラインを使用する場合は、APIリクエストで明示的に参照する必要があります。", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.customBadge": "カスタム", @@ -14764,7 +14723,6 @@ "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.fields.title": "フィールド", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.review.title": "見直し", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.test.title": "テスト(任意)", - "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.updateMappings.title": "マッピング", "xpack.enterpriseSearch.content.integrations.dropbox": "Dropbox", "xpack.enterpriseSearch.content.integrations.dropboxDescription": "Dropboxに保存されたファイルとフォルダーを検索します。", "xpack.enterpriseSearch.content.integrations.dropboxPaper": "Dropbox Paper", @@ -15336,7 +15294,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "サードパーティのデータソースからデータを抽出、変換、インデックス化、同期します。", "xpack.enterpriseSearch.ingestSelector.method.crawler": "Webクローラー", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "Webサイトやナレッジベースから検索可能なコンテンツを検出、抽出、インデックス化します。", - "xpack.enterpriseSearch.ingestSelector.startButton": "開始", "xpack.enterpriseSearch.inlineEditableTable.newRowButtonLabel": "新しい行", "xpack.enterpriseSearch.integrations.apiDescription": "Elasticsearchの堅牢なAPIを使用して、検索をアプリケーションに追加します。", "xpack.enterpriseSearch.integrations.apiName": "API", @@ -17921,7 +17878,6 @@ "xpack.fleet.initializationErrorMessageTitle": "Fleet を初期化できません", "xpack.fleet.integrations.customInputsLink": "カスタム入力", "xpack.fleet.integrations.discussForumLink": "フォーラム", - "xpack.fleet.integrations.endpointsButton": "エンドポイント", "xpack.fleet.integrations.installPackage.uploadedTooltip": "この統合はアップロードによってインストールされたため、自動的に再インストールできません。再インストールするには、もう一度アップロードしてください。", "xpack.fleet.integrations.integrationSaved": "統合設定が保存されました", "xpack.fleet.integrations.integrationSavedError": "統合設定の保存エラー", @@ -18650,9 +18606,7 @@ "xpack.grokDebugger.unknownErrorTitle": "問題が発生しました", "xpack.idxMgmt.badgeAriaLabel": "{label}。選択すると、これをフィルタリングします。", "xpack.idxMgmt.clearCacheIndicesAction.indexCacheClearedMessage": "インデックス{indexNames}のキャッシュがクリアされました。", - "xpack.idxMgmt.clearCacheIndicesAction.successMessage": "キャッシュが削除されました:[{indexNames}]", "xpack.idxMgmt.closeIndicesAction.indexClosedMessage": "インデックス{indexNames}は閉じられました。", - "xpack.idxMgmt.closeIndicesAction.successfullyClosedIndicesMessage": "[{indexNames}] がクローズされました", "xpack.idxMgmt.componentTemplateDetails.summaryTab.notInUseDescription": "インデックステンプレートを{createLink}するか、既存のテンプレートを{editLink}してください。", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaDescription": "クラスター状態に格納された、テンプレートに関する任意の情報。{learnMoreLink}", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaHelpText": "JSONフォーマットを使用:{code}", @@ -18671,7 +18625,6 @@ "xpack.idxMgmt.deleteDataStreamsConfirmationModal.multipleErrorsNotificationMessageText": "{count}データストリームの削除エラー", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, other {#個のデータストリーム}}が削除されました", "xpack.idxMgmt.deleteIndicesAction.indexDeletedMessage": "インデックス{indexNames}が削除されました。", - "xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage": "[{indexNames}] が削除されました", "xpack.idxMgmt.deleteTemplatesModal.confirmButtonLabel": "{numTemplatesToDelete, plural, other {テンプレート}}削除", "xpack.idxMgmt.deleteTemplatesModal.deleteDescription": "{numTemplatesToDelete, plural, other {これらのテンプレート}}を削除しようとしています:", "xpack.idxMgmt.deleteTemplatesModal.modalTitleText": "{numTemplatesToDelete, plural, other {#個のテンプレート}}削除", @@ -18689,9 +18642,7 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryHelpText": "デフォルトは{code}クエリです。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type}は、番号、日付、またはIPアドレスの範囲と一致します。", "xpack.idxMgmt.flushIndicesAction.indexFlushedMessage": "インデックス{indexNames}がフラッシュされました。", - "xpack.idxMgmt.flushIndicesAction.successfullyFlushedIndicesMessage": "[{indexNames}]がフラッシュされました", "xpack.idxMgmt.forceMergeIndicesAction.indexForcemergedMessage": "インデックス{indexNames}は強制的にマージされました。", - "xpack.idxMgmt.forceMergeIndicesAction.successfullyForceMergedIndicesMessage": "[{indexNames}]が強制結合されました", "xpack.idxMgmt.formWizard.stepAliases.aliasesEditorHelpText": "JSONフォーマットを使用:{code}", "xpack.idxMgmt.formWizard.stepSettings.settingsEditorHelpText": "JSONフォーマットを使用:{code}", "xpack.idxMgmt.goToDiscover.showIndexToolTip": "Discoverで{indexName}を表示", @@ -18788,9 +18739,7 @@ "xpack.idxMgmt.mappingsEditor.sourceFieldDescription": "_source フィールドには、インデックスの時点で指定された元の JSON ドキュメント本文が含まれています。_sourceフィールドに含めるか除外するフィールドを定義することで、_sourceフィールドから個別のフィールドを削除することができます。{docsLink}", "xpack.idxMgmt.mappingsEditor.typeField.documentationLinkLabel": "{typeName} ドキュメント", "xpack.idxMgmt.openIndicesAction.indexOpenedMessage": "インデックス{indexNames}は開かれました。", - "xpack.idxMgmt.openIndicesAction.successfullyOpenedIndicesMessage": "[{indexNames}] が開かれました", "xpack.idxMgmt.refreshIndicesAction.indexRefreshedMessage": "インデックス{indexNames}は更新されました。", - "xpack.idxMgmt.refreshIndicesAction.successfullyRefreshedIndicesMessage": "[{indexNames}] が更新されました", "xpack.idxMgmt.templateDetails.summaryTab.indexPatternsDescriptionListTitle": "インデックス{numIndexPatterns, plural, other {パターン}}", "xpack.idxMgmt.templateForm.stepLogistics.dataStreamDescription": "テンプレートは、インデックスではなく、データストリームを作成します。{docsLink}", "xpack.idxMgmt.templateForm.stepLogistics.fieldIndexPatternsHelpText": "スペースと{invalidCharactersList}文字は使用できません。", @@ -20358,62 +20307,59 @@ "xpack.infra.appName": "インフラログ", "xpack.infra.assetDetails.alerts.tooltip.documentationLink": "ドキュメンテーション", "xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "すべて表示", - "xpack.infra.assetDetails.formulas.cpuUsage": "CPU使用状況", - "xpack.infra.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", - "xpack.infra.assetDetails.formulas.cpuUsage.irqLabel": "irq", - "xpack.infra.assetDetails.formulas.cpuUsage.niceLabel": "nice", - "xpack.infra.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", - "xpack.infra.assetDetails.formulas.cpuUsage.stealLabel": "steal", - "xpack.infra.assetDetails.formulas.cpuUsage.systemLabel": "システム", - "xpack.infra.assetDetails.formulas.cpuUsage.userLabel": "ユーザー", - "xpack.infra.assetDetails.formulas.diskIORead": "ディスク読み取りIOPS", - "xpack.infra.assetDetails.formulas.diskIOWrite": "ディスク書き込みIOPS", - "xpack.infra.assetDetails.formulas.diskReadThroughput": "ディスク読み取りスループット", - "xpack.infra.assetDetails.formulas.diskSpaceAvailability": "空きディスク容量", - "xpack.infra.assetDetails.formulas.diskSpaceAvailable": "空きディスク容量", - "xpack.infra.assetDetails.formulas.diskUsage": "ディスク使用量", - "xpack.infra.assetDetails.formulas.diskWriteThroughput": "ディスク書き込みスループット", - "xpack.infra.assetDetails.formulas.hostCount.hostsLabel": "ホスト", - "xpack.infra.assetDetails.formulas.kubernetes.capacity": "容量", - "xpack.infra.assetDetails.formulas.kubernetes.used": "使用中", - "xpack.infra.assetDetails.formulas.load15m": "読み込み(15m)", - "xpack.infra.assetDetails.formulas.load1m": "読み込み(1m)", - "xpack.infra.assetDetails.formulas.load5m": "読み込み(5m)", - "xpack.infra.assetDetails.formulas.logRate": "ログレート", - "xpack.infra.assetDetails.formulas.memoryFree": "空きメモリー", - "xpack.infra.assetDetails.formulas.memoryUsage": "メモリー使用状況", - "xpack.infra.assetDetails.formulas.metric.label.cache": "キャッシュ", - "xpack.infra.assetDetails.formulas.metric.label.free": "空き", - "xpack.infra.assetDetails.formulas.metric.label.used": "使用中", - "xpack.infra.assetDetails.formulas.normalizedLoad1m": "正規化された負荷", - "xpack.infra.assetDetails.formulas.rx": "ネットワーク受信(RX)", - "xpack.infra.assetDetails.formulas.tx": "ネットワーク送信(TX)", + "xpack.metricsData.assetDetails.formulas.cpuUsage": "CPU使用状況", + "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", + "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", + "xpack.metricsData.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", + "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "システム", + "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "ユーザー", + "xpack.metricsData.assetDetails.formulas.diskIORead": "ディスク読み取りIOPS", + "xpack.metricsData.assetDetails.formulas.diskIOWrite": "ディスク書き込みIOPS", + "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "ディスク読み取りスループット", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "空きディスク容量", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "空きディスク容量", + "xpack.metricsData.assetDetails.formulas.diskUsage": "ディスク使用量", + "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "ディスク書き込みスループット", + "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "ホスト", + "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "容量", + "xpack.metricsData.assetDetails.formulas.kubernetes.used": "使用中", + "xpack.metricsData.assetDetails.formulas.load15m": "読み込み(15m)", + "xpack.metricsData.assetDetails.formulas.load1m": "読み込み(1m)", + "xpack.metricsData.assetDetails.formulas.load5m": "読み込み(5m)", + "xpack.metricsData.assetDetails.formulas.logRate": "ログレート", + "xpack.metricsData.assetDetails.formulas.memoryFree": "空きメモリー", + "xpack.metricsData.assetDetails.formulas.memoryUsage": "メモリー使用状況", + "xpack.metricsData.assetDetails.formulas.metric.label.cache": "キャッシュ", + "xpack.metricsData.assetDetails.formulas.metric.label.free": "空き", + "xpack.metricsData.assetDetails.formulas.metric.label.used": "使用中", + "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "正規化された負荷", + "xpack.metricsData.assetDetails.formulas.rx": "ネットワーク受信(RX)", + "xpack.metricsData.assetDetails.formulas.tx": "ネットワーク送信(TX)", + "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "Disk IOPS", + "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "Disk Throughput", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage": "ディスク使用量", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.available": "利用可能", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "使用中", + "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "マウントポイント別ディスク使用量", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "ノード CPU 処理能力", + "xpack.metricsData.assetDetails.metricsCharts.load": "読み込み", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "キャッシュ", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "空き", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "読み取り", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "使用中", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "書き込み", + "xpack.metricsData.assetDetails.metricsCharts.network": "ネットワーク", + "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "受信(RX)", + "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "送信(TX)", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "ノードディスク容量", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "ノードメモリー容量", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "ノードポッド容量", + "xpack.metricsData.assetDetails.overview.kpi.subtitle.average": "平均", "xpack.infra.assetDetails.header.return": "戻る", "xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name", "xpack.infra.assetDetails.metadata.tooltip.metadata": "メタデータ", - "xpack.infra.assetDetails.metricsCharts.cpuUsage": "CPU使用状況", - "xpack.infra.assetDetails.metricsCharts.diskIOPS": "Disk IOPS", - "xpack.infra.assetDetails.metricsCharts.diskThroughput": "Disk Throughput", - "xpack.infra.assetDetails.metricsCharts.diskUsage": "ディスク使用量", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.available": "利用可能", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.used": "使用中", - "xpack.infra.assetDetails.metricsCharts.diskUsageByMountingPoint": "マウントポイント別ディスク使用量", - "xpack.infra.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "ノード CPU 処理能力", - "xpack.infra.assetDetails.metricsCharts.load": "読み込み", - "xpack.infra.assetDetails.metricsCharts.logRate": "ログレート", - "xpack.infra.assetDetails.metricsCharts.memoryUsage": "メモリー使用状況", - "xpack.infra.assetDetails.metricsCharts.metric.label.cache": "キャッシュ", - "xpack.infra.assetDetails.metricsCharts.metric.label.free": "空き", - "xpack.infra.assetDetails.metricsCharts.metric.label.read": "読み取り", - "xpack.infra.assetDetails.metricsCharts.metric.label.used": "使用中", - "xpack.infra.assetDetails.metricsCharts.metric.label.write": "書き込み", - "xpack.infra.assetDetails.metricsCharts.network": "ネットワーク", - "xpack.infra.assetDetails.metricsCharts.network.label.rx": "受信(RX)", - "xpack.infra.assetDetails.metricsCharts.network.label.tx": "送信(TX)", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeDiskCapacity": "ノードディスク容量", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeMemoryCapacity": "ノードメモリー容量", - "xpack.infra.assetDetails.metricsCharts.nginx.nodePodCapacity": "ノードポッド容量", - "xpack.infra.assetDetails.metricsCharts.normalizedLoad1m": "正規化された負荷", "xpack.infra.assetDetails.overview.alertsSectionTitle": "アラート", "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Kubernetes概要", "xpack.infra.assetDetails.overview.metadataSectionTitle": "メタデータ", @@ -20426,11 +20372,6 @@ "xpack.infra.assetDetailsEmbeddable.displayName": "アセット詳細", "xpack.infra.assetDetailsEmbeddable.metadataSummary.showAllMetadataButton": "すべて表示", "xpack.infra.assetDetailsEmbeddable.notApplicableLabel": "N/A", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.cpuUsage.title": "CPU使用状況", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.diskUsage.title": "ディスク使用量", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.memoryUsage.title": "メモリー使用状況", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.normalizedLoad1m.title": "正規化された負荷", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.subtitle.average": "平均", "xpack.infra.assetDetailsEmbeddable.overview.metadataCloudProviderHeading": "クラウドプロバイダー", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostIpHeading": "ホスト IP", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostOsVersionHeading": "ホストOSバージョン", @@ -20548,19 +20489,7 @@ "xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "ログエントリーを検索...", "xpack.infra.hostsViewPage.tabs.logs.title": "ログ", "xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "Lensで開く", - "xpack.infra.hostsViewPage.tabs.metricsCharts.cpuUsage": "CPU使用状況", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIORead": "ディスク読み取りIOPS", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIOWrite": "ディスク書き込みIOPS", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskReadThroughput": "ディスク読み取りスループット", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceAvailable": "空きディスク容量", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskUsage": "ディスク使用量", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskWriteThroughput": "ディスク書き込みスループット", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryFree": "空きメモリー", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryUsage": "メモリー使用状況", - "xpack.infra.hostsViewPage.tabs.metricsCharts.normalizedLoad1m": "正規化された負荷", - "xpack.infra.hostsViewPage.tabs.metricsCharts.rx": "ネットワーク受信(RX)", "xpack.infra.hostsViewPage.tabs.metricsCharts.title": "メトリック", - "xpack.infra.hostsViewPage.tabs.metricsCharts.tx": "ネットワーク送信(TX)", "xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "これらのメトリックは何か。", "xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "点線が表示されている理由", "xpack.infra.infra.assetDetails.alerts.createAlertLink": "ルールを作成", @@ -22363,7 +22292,6 @@ "xpack.lens.config.configFlyoutCallout": "現在、ES|QLでは、構成オプションは限られています。", "xpack.lens.config.editLabel": "構成の編集", "xpack.lens.config.editLinkLabel": "Lensで編集", - "xpack.lens.config.editVisualizationLabel": "ビジュアライゼーションを編集", "xpack.lens.config.experimentalLabel": "テクニカルプレビュー", "xpack.lens.configPanel.addLayerButton": "レイヤーを追加", "xpack.lens.configPanel.experimentalLabel": "テクニカルプレビュー", @@ -24374,7 +24302,6 @@ "xpack.ml.dataframe.analytics.create.analyticsProgressErrorMessage": "分析ジョブ{jobId}の進行状況統計の取得中にエラーが発生しました", "xpack.ml.dataframe.analytics.create.configDetails.includedFieldsAndMoreDescription": "{includedFields} ...(およびその他{extraCount})", "xpack.ml.dataframe.analytics.create.createDataViewSuccessMessage": "Kibanaデータビュー{dataViewName}が作成されました。", - "xpack.ml.dataframe.analytics.create.dataViewExistsError": "{title}というタイトルのデータビューはすでに存在します。", "xpack.ml.dataframe.analytics.create.dependentVariableMaxDistictValuesError": "無効です。{message}", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.description": "分析ジョブ結果からKibanaのダッシュボード、Discover、その他のWebページへのリンクを提供します。{learnMoreLink}", "xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessageError": "データビュー{dataViewName}はすでに存在します。", @@ -25238,8 +25165,6 @@ "xpack.ml.dataframe.analytics.create.configDetails.standardizationEnabled": "標準化が有効です", "xpack.ml.dataframe.analytics.create.configDetails.trainingPercent": "トレーニングパーセンテージ", "xpack.ml.dataframe.analytics.create.createDataViewErrorMessage": "Kibanaデータビューの作成中にエラーが発生しました。", - "xpack.ml.dataframe.analytics.create.createDataViewLabel": "データビューを作成", - "xpack.ml.dataframe.analytics.create.dataViewPermissionWarning": "データビューを作成する権限が必要です。", "xpack.ml.dataframe.analytics.create.dependentVariableClassificationPlaceholder": "予測する数値、カテゴリ、ブール値フィールドを選択します。", "xpack.ml.dataframe.analytics.create.dependentVariableInputAriaLabel": "従属変数として使用するフィールドを入力してください。", "xpack.ml.dataframe.analytics.create.dependentVariableLabel": "従属変数", @@ -25247,18 +25172,11 @@ "xpack.ml.dataframe.analytics.create.dependentVariableOptionsNoNumericalFields": "このデータビューの数値型フィールドが見つかりませんでした。", "xpack.ml.dataframe.analytics.create.dependentVariableRegressionPlaceholder": "予測する数値フィールドを選択します。", "xpack.ml.dataframe.analytics.create.destinationIndexHelpText": "この名前のインデックスがすでに存在します。この分析ジョブを実行すると、デスティネーションインデックスが変更されます。", - "xpack.ml.dataframe.analytics.create.destinationIndexInputAriaLabel": "固有の宛先インデックス名を選択してください。", - "xpack.ml.dataframe.analytics.create.destinationIndexInvalidError": "無効なデスティネーションインデックス名。", - "xpack.ml.dataframe.analytics.create.destinationIndexLabel": "デスティネーションインデックス", "xpack.ml.dataframe.analytics.create.destinationIndexNotCreatedForDataFrameAnalyticsJob": "デスティネーションインデックスはまだ作成されていません。", - "xpack.ml.dataframe.analytics.create.DestIndexSameAsIdLabel": "ジョブIDと同じデスティネーションインデックス", "xpack.ml.dataframe.analytics.create.detailsDetails.editButtonText": "編集", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrls.title": "カスタムURL", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.learnMoreLinkText": "詳細", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSectionButton": "追加設定", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldHelpText": "グローバル時間フィルターで使用するためのプライマリ時間フィールドを選択してください。", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldLabel": "Kibanaインデックスパターンデータビューの時間フィールド", - "xpack.ml.dataframe.analytics.create.detailsStep.noTimeFieldOptionLabel": "時間フィールドオプションを使用しない", "xpack.ml.dataframe.analytics.create.downsampleFactorInputAriaLabel": "ツリー学習の損失関数の導関数を計算するために使用されるデータの比率。", "xpack.ml.dataframe.analytics.create.downsampleFactorLabel": "ダウンサンプリング係数", "xpack.ml.dataframe.analytics.create.downsampleFactorText": "ツリー学習の損失関数の導関数を計算するために使用されるデータの比率。0~1の範囲でなければなりません。", @@ -25345,9 +25263,8 @@ "xpack.ml.dataFrame.analytics.create.searchSelection.CcsErrorCallOutTitle": "クラスター横断検索を使用するデータビューはサポートされていません。", "xpack.ml.dataFrame.analytics.create.searchSelection.errorGettingDataViewTitle": "保存された検索で使用されているデータビューの読み込みエラー", "xpack.ml.dataFrame.analytics.create.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", - "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.indexPattern": "データビュー", + "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.dataView": "データビュー", "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.search": "保存検索", - "xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage": "デスティネーションインデックスのデータビューが作成されていない場合は、ジョブ結果を表示できないことがあります。", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitInputAriaLabel": "この深さを超える決定木は、損失計算でペナルティがあります。", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitLabel": "ソフトツリー深さ上限値", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitText": "この深さを超える決定木は、損失計算でペナルティがあります。0以上でなければなりません。", @@ -25562,7 +25479,6 @@ "xpack.ml.dataframe.analyticsMap.noJobSelectedLabel": "分析IDが選択されていません", "xpack.ml.dataframe.analyticsMap.title": "分析のマップ", "xpack.ml.dataframe.analyticsSourceSelection.title": "新しい分析ジョブ/ソースデータビューを選択", - "xpack.ml.dataframe.stepDetailsForm.destinationIndexInvalidErrorLink": "インデックス名の制限に関する詳細。", "xpack.ml.dataFrameAnalytics.analyticsMap.docTitle": "分析マップ", "xpack.ml.dataFrameAnalytics.createJob.docTitle": "ジョブの作成", "xpack.ml.dataFrameAnalytics.exploration.docTitle": "結果エクスプローラー", @@ -29234,7 +29150,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.customEquationTooltip": "これは基本的な数学ロジック(A + B / C)とブールロジック(A < B ?A :B)をサポートします。", "xpack.observability.customThreshold.rule.alertFlyout.dataViewError.noTimestamp": "選択したデータビューにタイムスタンプフィールドがありません。他のデータビューを選択してください。", "xpack.observability.customThreshold.rule.alertFlyout.defineTextQueryPrompt": "クエリフィルターを定義(任意)", - "xpack.observability.customThreshold.rule.alertFlyout.error.aggregationRequired": "集約が必要です。", "xpack.observability.customThreshold.rule.alertFlyout.error.equation.invalidCharacters": "等式フィールドでは次の文字のみを使用できます:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "フィルタークエリは無効です。", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "データビューが必要です。", @@ -32357,12 +32272,7 @@ "xpack.securitySolution.responseActionsList.list.recordRange": "{total} {recordsLabel}件中{range}を表示中", "xpack.securitySolution.responseActionsList.list.recordRangeLabel": "{records, plural, other {対応アクション}}", "xpack.securitySolution.riskInformation.howCalculatedText": "エンティティリスクエンジンは、過去30日間の\"オープン\"および\"確認済み\"アラートを集約し、ホストまたはユーザーにリスクスコアを割り当てるために1時間ごとに実行されます。次に、個々のリスクスコアを集約し、{riemannZetaLink}を使用して0~100の範囲に正規化します。", - "xpack.securitySolution.riskInformation.learnMore": "{riskEntity}リスクの詳細", "xpack.securitySolution.riskInformation.riskHeader": "{riskEntity}リスクスコア範囲", - "xpack.securitySolution.riskInformation.riskScoreFieldLabel": "{riskEntity}リスクスコア", - "xpack.securitySolution.riskInformation.riskScoreFieldText": "{riskScoreField}フィールドは{riskEntity}の正規化されたリスクを1つの数値として表します。この値は、トリアージとレスポンスのプレイブックでリスクの相対的な指標として使用できます。", - "xpack.securitySolution.riskInformation.riskScoreLevelLabel": "{riskEntity}リスクレベル", - "xpack.securitySolution.riskInformation.riskScoreLevelText": "{riskLevelField}フィールドは、定義済みのリスクメトリックに基づく{riskEntity}の6つのリスクレベルのうちの1つを表します。", "xpack.securitySolution.riskScore.api.ingestPipeline.delete.errorMessageTitle": "インジェスト{totalCount, plural, =1 {パイプライン} other {パイプライン}}を削除できませんでした", "xpack.securitySolution.riskScore.api.transforms.delete.errorMessageTitle": "{totalCount, plural, =1 {変換} other {変換}}の削除に失敗しました", "xpack.securitySolution.riskScore.api.transforms.start.errorMessageTitle": "{totalCount, plural, =1 {変換} other {変換}}の開始に失敗しました", @@ -36716,7 +36626,7 @@ "xpack.securitySolution.timeline.saveTimeline.modal.header": "タイムラインを保存", "xpack.securitySolution.timeline.saveTimeline.modal.optionalLabel": "オプション", "xpack.securitySolution.timeline.saveTimeline.modal.titleAriaLabel": "タイトル", - "xpack.securitySolution.timeline.saveTimeline.modal.titleTitle": "タイトル", + "xpack.securitySolution.timeline.saveTimeline.modal.title": "タイトル", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.discard.title": "タイムラインテンプレートを破棄", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.header": "タイムラインテンプレートを保存", "xpack.securitySolution.timeline.searchOrFilter.filterDescription": "上のデータプロバイダーからのイベントは、隣接の KQL でフィルターされます", @@ -40585,7 +40495,6 @@ "xpack.transform.stepCreateForm.createAlertRuleDescription": "ウィザードを開き、トランスフォームの正常性を監視するアラートルールを作成します。", "xpack.transform.stepCreateForm.createAndStartTransformButton": "作成して開始", "xpack.transform.stepCreateForm.createAndStartTransformDescription": "トランスフォームを作成して開始します。トランスフォームは、クラスターの検索とインデックスによる負荷を増やします。過剰な負荷が生じた場合はトランスフォームを停止してください。トランスフォームの開始後、トランスフォームの閲覧を続けるオプションが提供されます。", - "xpack.transform.stepCreateForm.createDataViewLabel": "Kibanaデータビューを作成", "xpack.transform.stepCreateForm.createTransformButton": "作成", "xpack.transform.stepCreateForm.createTransformDescription": "開始せずにトランスフォームを作成します。トランスフォームは後ほどトランスフォームリストに戻って開始できます。", "xpack.transform.stepCreateForm.creatingDataViewMessage": "Kibanaデータビューを作成しています...", @@ -40671,15 +40580,7 @@ "xpack.transform.stepDetailsForm.continuousModeDelayLabel": "遅延", "xpack.transform.stepDetailsForm.continuousModeError": "日付フィールドがないインデックスでは、連続モードを使用できません。", "xpack.transform.stepDetailsForm.createIndexAPI": "インデックスAPIの作成", - "xpack.transform.stepDetailsForm.dataViewPermissionWarning": "データビューを作成する権限が必要です。", - "xpack.transform.stepDetailsForm.dataViewTimeFieldHelpText": "グローバル時間フィルターで使用するためのプライマリ時間フィールドを選択してください。", - "xpack.transform.stepDetailsForm.dataViewTimeFieldLabel": "Kibanaインデックスパターンデータビューの時間フィールド", - "xpack.transform.stepDetailsForm.dataViewTitleError": "このタイトルのデータビューはすでに存在します。", "xpack.transform.stepDetailsForm.destinationIndexHelpText": "この名前のインデックスがすでに存在します。このトランスフォームを実行すると、デスティネーションインデックスが変更されます。", - "xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel": "固有の宛先インデックス名を選択してください。", - "xpack.transform.stepDetailsForm.destinationIndexInvalidError": "無効なデスティネーションインデックス名。", - "xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink": "インデックス名の制限に関する詳細。", - "xpack.transform.stepDetailsForm.destinationIndexLabel": "デスティネーションインデックス", "xpack.transform.stepDetailsForm.destinationIngestPipelineAriaLabel": "インジェストパイプラインを選択(任意)", "xpack.transform.stepDetailsForm.destinationIngestPipelineComboBoxPlaceholder": "インジェストパイプラインを選択(任意)", "xpack.transform.stepDetailsForm.destinationIngestPipelineLabel": "デスティネーションインジェストパイプライン", @@ -40698,7 +40599,6 @@ "xpack.transform.stepDetailsForm.maxPageSearchSizeHelpText": "各チェックポイントの複合集計で使用する、初期ページサイズ。", "xpack.transform.stepDetailsForm.maxPageSearchSizeLabel": "最大ページ検索サイズ", "xpack.transform.stepDetailsForm.missingBucketCheckboxHelpTextLink": "詳細", - "xpack.transform.stepDetailsForm.noTimeFieldOptionLabel": "時間フィールドオプションを使用しない", "xpack.transform.stepDetailsForm.numFailureRetriesAriaLabel": "最大再試行回数を選択します。", "xpack.transform.stepDetailsForm.NumFailureRetriesError": "再試行回数は0~100の範囲でなければなりません。-1を指定すると、再試行回数が無制限に設定されます。", "xpack.transform.stepDetailsForm.retentionPolicyDateFieldHelpText": "デスティネーションインデックスで古いドキュメントを特定するために使用できる日付フィールドを選択します。", @@ -43033,7 +42933,6 @@ "cloud.deploymentDetails.cloudIDLabel": "クラウドID", "cloud.deploymentDetails.createManageApiKeysButtonLabel": "APIキーの作成と管理", "cloud.deploymentDetails.elasticEndpointLabel": "Elastic Endpoint", - "cloud.deploymentDetails.helpMenuLinks.endpoints": "エンドポイント", "cloud.deploymentDetails.learnMoreButtonLabel": "詳細", "cloud.deploymentDetails.modal.closeButtonLabel": "閉じる", "cloud.deploymentDetails.modal.learnMoreButtonLabel": "詳細", @@ -43471,7 +43370,6 @@ "unifiedDocViewer.sourceViewer.errorMessageTitle": "エラーが発生しました", "unifiedDocViewer.sourceViewer.refresh": "更新", "utils.filename.pathWarning": "パスの形式が正しくない可能性があります。値を検証してください", - "utils.filename.wildcardWarning": "ファイルパスでワイルドカードを使用すると、エンドポイントのパフォーマンスに影響する可能性があります", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "Visualizeでゲージグラフのレガシーグラフライブラリを有効にします。", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "ゲージグラフのレガシーグラフライブラリ", "visTypeGauge.controls.gaugeOptions.alignmentLabel": "アラインメント", @@ -43624,7 +43522,6 @@ "xpack.cloudDataMigration.upgrade.text": "新しいバージョンへのアップグレードがより簡単に", "xpack.cloudLinks.deploymentLinkLabel": "このデプロイの管理", "xpack.cloudLinks.helpMenuLinks.documentation": "ドキュメント", - "xpack.cloudLinks.helpMenuLinks.endpoints": "エンドポイント", "xpack.cloudLinks.helpMenuLinks.giveFeedback": "フィードバックを作成する", "xpack.cloudLinks.helpMenuLinks.support": "サポート", "xpack.cloudLinks.setupGuide": "セットアップガイド", @@ -43740,4 +43637,4 @@ "xpack.serverlessObservability.nav.projectSettings": "プロジェクト設定", "xpack.serverlessObservability.nav.visualizations": "ビジュアライゼーション" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 879ccde09a001..916c2eba4d8e9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2065,7 +2065,6 @@ "data.search.esErrorTitle": "无法检索搜索结果", "data.search.esql.help": "使用 ES|QL 查询 Elasticsearch。", "data.search.esql.query.help": "ES|QL 查询。", - "data.search.esql.timezone.help": "要用于日期操作的时区。有效的 ISO8601 格式和 UTC 偏移均适用。", "data.search.essql.count.help": "要检索的文档数目。要获取更佳的性能,请使用较小的数据集。", "data.search.essql.help": "使用 Elasticsearch SQL 查询 Elasticsearch。", "data.search.essql.locale.help": "要使用的区域设置。", @@ -5322,7 +5321,6 @@ "savedSearch.kibana_context.savedSearchId.help": "指定要用于查询和筛选的已保存搜索 ID", "savedSearch.kibana_context.timeRange.help": "指定 Kibana 时间范围筛选", "searchApiPanels.welcomeBanner.header.greeting.customTitle": "{name}您好!", - "searchApiPanels.welcomeBanner.ingestData.clientDocLink": "{languageName} API 参考", "searchApiPanels.welcomeBanner.installClient.clientDocLink": "{languageName} 客户端文档", "searchApiPanels.welcomeBanner.selectClient.description": "Elastic 以几种流行语言构建和维护客户端,我们的社区也做出了许多贡献。选择您常用的语言客户端或深入分析 {console} 以开始使用。", "searchApiPanels.welcomeBanner.codeBox.copyButtonLabel": "复制", @@ -5330,22 +5328,7 @@ "searchApiPanels.welcomeBanner.header.description": "设置您的编程语言客户端,采集一些数据,如此即可在数分钟内开始搜索。", "searchApiPanels.welcomeBanner.header.greeting.defaultTitle": "您好!", "searchApiPanels.welcomeBanner.header.title": "Elasticsearch 入门", - "searchApiPanels.welcomeBanner.ingestData.beatsDescription": "用于 Elasticsearch 的轻量级、单一用途数据采集器。使用 Beats 从您的服务器发送运营数据。", - "searchApiPanels.welcomeBanner.ingestData.beatsLink": "Beats", - "searchApiPanels.welcomeBanner.ingestData.beatsTitle": "Beats", - "searchApiPanels.welcomeBanner.ingestData.connectorsDescription": "用于将数据从第三方源同步到 Elasticsearch 的专用集成。使用 Elastic 连接器同步来自一系列数据库和对象存储的内容。", - "searchApiPanels.welcomeBanner.ingestData.connectorsPythonLink": "connectors-python", - "searchApiPanels.welcomeBanner.ingestData.connectorsTitle": "连接器客户端", "searchApiPanels.welcomeBanner.ingestData.description": "将数据添加到数据流或索引,使其可进行搜索。选择适合您的应用程序和工作流的集成方法。", - "searchApiPanels.welcomeBanner.ingestData.ingestApiDescription": "最灵活的数据索引方法,允许您全面控制定制和优化选项。", - "searchApiPanels.welcomeBanner.ingestData.ingestApiLabel": "通过 API 采集", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationDescription": "针对转换数据并将其传输到 Elasticsearch 而优化的专用采集工具。", - "searchApiPanels.welcomeBanner.ingestData.ingestIntegrationLabel": "通过集成采集", - "searchApiPanels.welcomeBanner.ingestData.ingestLegendLabel": "选择采集方法", - "searchApiPanels.welcomeBanner.ingestData.integrationsLink": "关于集成", - "searchApiPanels.welcomeBanner.ingestData.logstashDescription": "将数据添加到数据流或索引,使其可进行搜索。选择适合您的应用程序和工作流的集成方法。", - "searchApiPanels.welcomeBanner.ingestData.logstashLink": "Logstash", - "searchApiPanels.welcomeBanner.ingestData.logstashTitle": "Logstash", "searchApiPanels.welcomeBanner.ingestData.title": "采集数据", "searchApiPanels.welcomeBanner.installClient.description": "Elastic 以几种流行语言构建和维护客户端,我们的社区也做出了许多贡献。安装您常用的语言客户端以开始使用。", "searchApiPanels.welcomeBanner.installClient.title": "安装客户端", @@ -12871,7 +12854,6 @@ "xpack.dataVisualizer.table.expandRowScreenMsg": "展开行", "xpack.dataVisualizer.title": "上传文件", "xpack.elasticAssistant.assistant.connectors.connectorMissingCallout.calloutDescription": "在上方或从 {link} 中选择连接器以继续", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactText": "有趣的事实:一旦完成,请查看 Kibana 服务器日志以了解进度,并 {funFacts} 以在 Discover 中查看结果。将花费(许多)分钟,具体取决于数据集,而且,关闭此对话框将取消评估!", "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "在 {machineLearning} 中配置 ELSER 以开始。{seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "已初始化为 `{kbIndexPattern}`", "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "来自 AI 系统的响应可能不会始终完全准确。有关辅助功能及其用法的详细信息,请参阅 {documentationLink}。", @@ -12999,7 +12981,6 @@ "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationPromptLabel": "评估提示", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeDescription": "要执行的评估类型,如“正确性”、“esql 验证器”或“定制”,并提供您自己的评估提示", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluationTypeLabel": "评估类型", - "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetDescription": "要评估的样例数据集。具有“input”和“references”属性的对象数组", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorDatasetLabel": "数据集", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorFunFactDiscoverLinkText": "单击此处", "xpack.elasticAssistant.assistant.settings.evaluationSettings.evaluatorModelDescription": "要执行最后评估的模型", @@ -13162,7 +13143,6 @@ "xpack.enterpriseSearch.content.index.connector.syncRules.flyout.errorTitle": "同步{idsLength, plural, other {规则}} {ids}{idsLength, plural, other {有}}无效。", "xpack.enterpriseSearch.content.index.pipelines.copyCustomizeCallout.description": "您的索引正使用默认采集管道 {defaultPipeline}。将该管道复制到特定于索引的配置中,以解锁创建定制采集和推理管道的功能。", "xpack.enterpriseSearch.content.index.pipelines.ingestFlyout.modalBodyAPIText": "{apiIndex}对以下设置所做的更改仅供参考。这些设置不会持续用于您的索引或管道。", - "xpack.enterpriseSearch.content.indices.callout.text": "您的 Elasticsearch 索引如今在 Search 中位于前排和中心位置。您可以创建新索引,直接通过它们构建搜索体验。有关如何在 Search 中使用 Elasticsearch 索引的详情,{docLink}", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.description": "首先,生成一个 Elasticsearch API 密钥。此 {apiKeyName} 密钥将为连接器启用读取和写入权限,以便将文档索引到已创建的 {indexName} 索引。请将该密钥保存到安全位置,因为您需要它来配置连接器。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorConnected": "您的连接器 {name} 已成功连接到 Search。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.secondParagraph": "连接器存储库包含几个 {link}。使用我们的框架可加速为定制数据源开发连接器。", @@ -14378,8 +14358,6 @@ "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.done": "完成", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.generateButton": "生成密钥", "xpack.enterpriseSearch.content.analytics.api.generateAnalyticsApiKeyModal.title": "创建分析 API 密钥", - "xpack.enterpriseSearch.content.callout.dismissButton": "关闭", - "xpack.enterpriseSearch.content.callout.title": "在 Search 中引入 Elasticsearch 索引", "xpack.enterpriseSearch.content.cannotConnect.body": "更多信息。", "xpack.enterpriseSearch.content.cannotConnect.title": "无法连接到 Enterprise Search", "xpack.enterpriseSearch.content.crawler.authentication": "身份验证", @@ -14461,18 +14439,18 @@ "xpack.enterpriseSearch.content.index.pipelines.settings.reduceWhitespaceLabel": "减少空白", "xpack.enterpriseSearch.content.index.pipelines.settings.runMlInferenceDescrition": "使用兼容的已训练 ML 模型增强您的数据", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedBody": "您可以在单线程配置中启动模型以用于测试,或调整性能以用于生产环境。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedTitle": "您的 ELSER v2 模型已部署,但尚未启动。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployedTitle": "您的 ELSER 模型已部署,但尚未启动。", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingBody": "同时,您可以继续使用其他上传的模型来创建管道。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingTitle": "您的 ELSER v2 模型正在部署。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.deployingTitle": "您的 ELSER 模型正在部署。", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.dismissButton": "关闭 ELSER 对外调用", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.learnMoreLink": "了解详情", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedBody": "在您的定制推理管道中体验 ELSER v2 的强大功能。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedBody": "在您的定制推理管道中体验 ELSER 的强大功能。", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedBody": "此单线程配置非常适合测试您的定制推理管道,但应微调性能以用于生产。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitle": "您的 ELSER v2 模型已通过单线程方式启动。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitleCompact": "您的 ELSER v2 模型正通过单线程方式运行。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitle": "您的 ELSER v2 模型已启动。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitleCompact": "您的 ELSER v2 模型正在运行。", - "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title": "通过 ELSER v2 改进您的结果", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitle": "您的 ELSER 模型已通过单线程方式启动。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedSingleThreadedTitleCompact": "您的 ELSER 模型正通过单线程方式运行。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitle": "您的 ELSER 模型已启动。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.startedTitleCompact": "您的 ELSER 模型正在运行。", + "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.title": "通过 ELSER 改进您的结果", "xpack.enterpriseSearch.content.index.pipelines.textExpansionCallOut.titleBadge": "新建", "xpack.enterpriseSearch.content.index.searchApplication.createSearchApplication": "创建搜索应用程序", "xpack.enterpriseSearch.content.index.searchEngines.createEngineDisabledTooltip": "无法从隐藏索引创建搜索应用程序。", @@ -14481,7 +14459,6 @@ "xpack.enterpriseSearch.content.index.syncButton.label": "同步", "xpack.enterpriseSearch.content.index.syncButton.syncing.label": "正在同步", "xpack.enterpriseSearch.content.index.syncButton.waitingForSync.label": "等待同步", - "xpack.enterpriseSearch.content.indices.callout.docLink": "阅读文档", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.button.label": "生成 API 密钥", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.cancelButton.label": "取消", "xpack.enterpriseSearch.content.indices.configurationConnector.apiKey.confirmModal.confirmButton.label": "生成 API 密钥", @@ -14615,16 +14592,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.createErrors": "创建管道时出错", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.esDocs.link": "了解如何添加已训练模型", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.noModels.imageAlt": "无 Machine Learning 模型图示", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.chooseExistingLabel": "新建或现有", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.description": "构建或重复使用将在您的主管道中用作处理器的子管道。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionDeployTrainedModel": "要在集群中执行自然语言处理任务,必须部署适当的已训练模型。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.descriptionUsePipelines": "将保存您创建的管道,以便在 Elastic 部署中的其他位置使用。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.docsLink": "详细了解如何在 Search 中导入并使用 ML 模型", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.emptyValueError": "“字段”必填。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.chooseLabel": "选择", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.existingLabel": "现有管道", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.model": "模型", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.newLabel": "新建管道", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.placeholder": "选择一个", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipeline.sourceFields": "源字段", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.existingPipelineLabel": "选择现有推理管道", @@ -14634,11 +14604,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.invalidPipelineName": "名称必须仅包含字母、数字、下划线和连字符。", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.placeholder": "选择模型", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.model.redactedValue": "此模型在 Kibana 工作区不可用", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.modelLabel": "选择已训练 ML 模型", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.nameLabel": "名称", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.namePlaceholder": "为此管道输入唯一名称", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.pipelineNameExistsError": "名称已由其他管道使用。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.title": "创建或选择管道", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.titleSelectTrainedModel": "选择已训练 ML 模型", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions": "操作", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.fields.actions.deleteMapping": "删除此映射", @@ -14675,15 +14643,6 @@ "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.subtitle.result": "结果", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.title": "测试管道结果", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.test.useJsonFormat": "使用此 JSON 格式添加您自己的文档系列", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.description": "您必须先手动更新索引映射,然后才能开始通过管道索引文档。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.descriptionNoAction": "您的索引映射会自动进行更新,以包括选定的推理输出字段。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.docsLink": "了解详情", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappings": "字段映射", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.fieldMappingsRequired": "必填字段映射", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.presentInMapping": "确保映射中存在选定推理输出字段。", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.required": "必需", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.title": "更新索引映射", - "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.updateMappings.titleNoAction": "查看索引映射更新", "xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.title": "添加推理管道", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.apiIndexSubtitle": "采集管道将针对搜索应用程序优化您的索引。如果希望在基于 API 的索引中使用这些管道,您需要在 API 请求中显式引用它们。", "xpack.enterpriseSearch.content.indices.pipelines.ingestionPipeline.customBadge": "定制", @@ -14742,9 +14701,9 @@ "xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.startModelButton.label": "以单线程方式启动", "xpack.enterpriseSearch.content.indices.pipelines.textExpansionCallOut.viewModelsButton": "查看详情", "xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.mlNotificationsLink": "Machine Learning 通知", - "xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.title": "ELSER v2 部署出错", - "xpack.enterpriseSearch.content.indices.pipelines.textExpansionFetchError.title": "提取 ELSER v2 模型时出错", - "xpack.enterpriseSearch.content.indices.pipelines.textExpansionStartError.title": "启动 ELSER v2 部署时出错", + "xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.title": "ELSER 部署出错", + "xpack.enterpriseSearch.content.indices.pipelines.textExpansionFetchError.title": "提取 ELSER 模型时出错", + "xpack.enterpriseSearch.content.indices.pipelines.textExpansionStartError.title": "启动 ELSER 部署时出错", "xpack.enterpriseSearch.content.indices.searchIndex.convertConnector.buttonLabel": "转换连接器", "xpack.enterpriseSearch.content.indices.selectConnector.allConnectorsLabel": "所有连接器", "xpack.enterpriseSearch.content.indices.selectConnector.callout.description.connectorsClient": "连接器客户端", @@ -14764,7 +14723,6 @@ "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.fields.title": "字段", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.review.title": "复查", "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.test.title": "测试(可选)", - "xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.updateMappings.title": "映射", "xpack.enterpriseSearch.content.integrations.dropbox": "Dropbox", "xpack.enterpriseSearch.content.integrations.dropboxDescription": "搜索存储在 Dropbox 上的文件和文件夹。", "xpack.enterpriseSearch.content.integrations.dropboxPaper": "Dropbox Paper", @@ -15336,7 +15294,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "提取、转换、索引和同步来自第三方数据源的数据。", "xpack.enterpriseSearch.ingestSelector.method.crawler": "网络爬虫", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "发现、提取和索引网站和知识库中的可搜索内容。", - "xpack.enterpriseSearch.ingestSelector.startButton": "启动", "xpack.enterpriseSearch.inlineEditableTable.newRowButtonLabel": "新行", "xpack.enterpriseSearch.integrations.apiDescription": "通过 Elasticsearch 稳健的 API 将搜索功能添加到您的应用程序。", "xpack.enterpriseSearch.integrations.apiName": "API", @@ -17921,7 +17878,6 @@ "xpack.fleet.initializationErrorMessageTitle": "无法初始化 Fleet", "xpack.fleet.integrations.customInputsLink": "定制输入", "xpack.fleet.integrations.discussForumLink": "论坛", - "xpack.fleet.integrations.endpointsButton": "终端", "xpack.fleet.integrations.installPackage.uploadedTooltip": "此集成通过上传进行安装,因此无法自动重新安装。请再次将其上传,以便重新安装。", "xpack.fleet.integrations.integrationSaved": "已保存集成设置", "xpack.fleet.integrations.integrationSavedError": "保存集成设置时出错", @@ -18650,9 +18606,7 @@ "xpack.grokDebugger.unknownErrorTitle": "出问题了", "xpack.idxMgmt.badgeAriaLabel": "{label}。选择以基于其进行筛选。", "xpack.idxMgmt.clearCacheIndicesAction.indexCacheClearedMessage": "已清除索引 {indexNames} 的缓存。", - "xpack.idxMgmt.clearCacheIndicesAction.successMessage": "已成功清除缓存:[{indexNames}]", "xpack.idxMgmt.closeIndicesAction.indexClosedMessage": "索引 {indexNames} 已关闭。", - "xpack.idxMgmt.closeIndicesAction.successfullyClosedIndicesMessage": "已成功关闭:[{indexNames}]", "xpack.idxMgmt.componentTemplateDetails.summaryTab.notInUseDescription": "{createLink}索引模板或{editLink}现有索引模板。", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaDescription": "有关模板的任意信息,以集群状态存储。{learnMoreLink}", "xpack.idxMgmt.componentTemplateForm.stepLogistics.metaHelpText": "使用 JSON 格式:{code}", @@ -18671,7 +18625,6 @@ "xpack.idxMgmt.deleteDataStreamsConfirmationModal.multipleErrorsNotificationMessageText": "删除 {count} 个数据流时出错", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.successDeleteMultipleNotificationMessageText": "已删除 {numSuccesses, plural, other {# 个数据流}}", "xpack.idxMgmt.deleteIndicesAction.indexDeletedMessage": "索引 {indexNames} 已删除。", - "xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage": "已成功删除:[{indexNames}]", "xpack.idxMgmt.deleteTemplatesModal.confirmButtonLabel": "删除 {numTemplatesToDelete, plural, other {模板}}", "xpack.idxMgmt.deleteTemplatesModal.deleteDescription": "您即将删除{numTemplatesToDelete, plural, other {以下模板}}:", "xpack.idxMgmt.deleteTemplatesModal.modalTitleText": "删除 {numTemplatesToDelete, plural, other {# 个模板}}", @@ -18689,9 +18642,7 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryHelpText": "默认为:{code} 查询。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type} 匹配一个数字、日期或 IP 地址范围。", "xpack.idxMgmt.flushIndicesAction.indexFlushedMessage": "索引 {indexNames} 已清空。", - "xpack.idxMgmt.flushIndicesAction.successfullyFlushedIndicesMessage": "已成功清空:[{indexNames}]", "xpack.idxMgmt.forceMergeIndicesAction.indexForcemergedMessage": "已强制合并索引 {indexNames}。", - "xpack.idxMgmt.forceMergeIndicesAction.successfullyForceMergedIndicesMessage": "已成功强制合并:[{indexNames}]", "xpack.idxMgmt.formWizard.stepAliases.aliasesEditorHelpText": "使用 JSON 格式:{code}", "xpack.idxMgmt.formWizard.stepSettings.settingsEditorHelpText": "使用 JSON 格式:{code}", "xpack.idxMgmt.goToDiscover.showIndexToolTip": "在 Discover 中显示 {indexName}", @@ -18788,9 +18739,7 @@ "xpack.idxMgmt.mappingsEditor.sourceFieldDescription": "_source 字段包含在索引时提供的原始 JSON 文档正文。单个字段可通过定义哪些字段可以在 _source 字段中包括或排除来进行修剪。{docsLink}", "xpack.idxMgmt.mappingsEditor.typeField.documentationLinkLabel": "{typeName} 文档", "xpack.idxMgmt.openIndicesAction.indexOpenedMessage": "索引 {indexNames} 已打开。", - "xpack.idxMgmt.openIndicesAction.successfullyOpenedIndicesMessage": "已成功打开:[{indexNames}]", "xpack.idxMgmt.refreshIndicesAction.indexRefreshedMessage": "索引 {indexNames} 已刷新。", - "xpack.idxMgmt.refreshIndicesAction.successfullyRefreshedIndicesMessage": "已成功刷新:[{indexNames}]", "xpack.idxMgmt.templateDetails.summaryTab.indexPatternsDescriptionListTitle": "索引{numIndexPatterns, plural, other {模式}}", "xpack.idxMgmt.templateForm.stepLogistics.dataStreamDescription": "该模板创建数据流,而非索引。{docsLink}", "xpack.idxMgmt.templateForm.stepLogistics.fieldIndexPatternsHelpText": "不允许使用空格和字符 {invalidCharactersList}。", @@ -20358,62 +20307,58 @@ "xpack.infra.appName": "基础架构日志", "xpack.infra.assetDetails.alerts.tooltip.documentationLink": "文档", "xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "全部显示", - "xpack.infra.assetDetails.formulas.cpuUsage": "CPU 使用率", - "xpack.infra.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", - "xpack.infra.assetDetails.formulas.cpuUsage.irqLabel": "irq", - "xpack.infra.assetDetails.formulas.cpuUsage.niceLabel": "nice", - "xpack.infra.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", - "xpack.infra.assetDetails.formulas.cpuUsage.stealLabel": "steal", - "xpack.infra.assetDetails.formulas.cpuUsage.systemLabel": "system", - "xpack.infra.assetDetails.formulas.cpuUsage.userLabel": "用户", - "xpack.infra.assetDetails.formulas.diskIORead": "磁盘读取 IOPS", - "xpack.infra.assetDetails.formulas.diskIOWrite": "磁盘写入 IOPS", - "xpack.infra.assetDetails.formulas.diskReadThroughput": "磁盘读取吞吐量", - "xpack.infra.assetDetails.formulas.diskSpaceAvailability": "磁盘空间可用性", - "xpack.infra.assetDetails.formulas.diskSpaceAvailable": "可用磁盘空间", - "xpack.infra.assetDetails.formulas.diskUsage": "磁盘使用率", - "xpack.infra.assetDetails.formulas.diskWriteThroughput": "磁盘写入吞吐量", - "xpack.infra.assetDetails.formulas.hostCount.hostsLabel": "主机", - "xpack.infra.assetDetails.formulas.kubernetes.capacity": "容量", - "xpack.infra.assetDetails.formulas.kubernetes.used": "已使用", - "xpack.infra.assetDetails.formulas.load15m": "负载(15 分钟)", - "xpack.infra.assetDetails.formulas.load1m": "负载(1 分钟)", - "xpack.infra.assetDetails.formulas.load5m": "负载(5 分钟)", - "xpack.infra.assetDetails.formulas.logRate": "日志速率", - "xpack.infra.assetDetails.formulas.memoryFree": "可用内存", - "xpack.infra.assetDetails.formulas.memoryUsage": "内存利用率", - "xpack.infra.assetDetails.formulas.metric.label.cache": "缓存", - "xpack.infra.assetDetails.formulas.metric.label.free": "可用", - "xpack.infra.assetDetails.formulas.metric.label.used": "已使用", - "xpack.infra.assetDetails.formulas.normalizedLoad1m": "标准化负载", - "xpack.infra.assetDetails.formulas.rx": "网络入站数据 (RX)", - "xpack.infra.assetDetails.formulas.tx": "网络出站数据 (TX)", + "xpack.metricsData.assetDetails.formulas.cpuUsage": "CPU 使用率", + "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", + "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", + "xpack.metricsData.assetDetails.formulas.cpuUsage.softirqLabel": "softirq", + "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", + "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "system", + "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "用户", + "xpack.metricsData.assetDetails.formulas.diskIORead": "磁盘读取 IOPS", + "xpack.metricsData.assetDetails.formulas.diskIOWrite": "磁盘写入 IOPS", + "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "磁盘读取吞吐量", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "磁盘空间可用性", + "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "可用磁盘空间", + "xpack.metricsData.assetDetails.formulas.diskUsage": "磁盘使用率", + "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "磁盘写入吞吐量", + "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "主机", + "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "容量", + "xpack.metricsData.assetDetails.formulas.kubernetes.used": "已使用", + "xpack.metricsData.assetDetails.formulas.load15m": "负载(15 分钟)", + "xpack.metricsData.assetDetails.formulas.load1m": "负载(1 分钟)", + "xpack.metricsData.assetDetails.formulas.load5m": "负载(5 分钟)", + "xpack.metricsData.assetDetails.formulas.logRate": "日志速率", + "xpack.metricsData.assetDetails.formulas.memoryFree": "可用内存", + "xpack.metricsData.assetDetails.formulas.memoryUsage": "内存利用率", + "xpack.metricsData.assetDetails.formulas.metric.label.cache": "缓存", + "xpack.metricsData.assetDetails.formulas.metric.label.free": "可用", + "xpack.metricsData.assetDetails.formulas.metric.label.used": "已使用", + "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "标准化负载", + "xpack.metricsData.assetDetails.formulas.rx": "网络入站数据 (RX)", + "xpack.metricsData.assetDetails.formulas.tx": "网络出站数据 (TX)", + "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "磁盘 IOPS", + "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "磁盘吞吐量", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.available": "可用", + "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "已使用", + "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "磁盘使用率(按装载点)", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "节点 CPU 容量", + "xpack.metricsData.assetDetails.metricsCharts.load": "加载", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "缓存", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "可用", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "读取", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "已使用", + "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "写入", + "xpack.metricsData.assetDetails.metricsCharts.network": "网络", + "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "入站 (RX)", + "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "出站 (TX)", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "节点磁盘容量", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "节点内存容量", + "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "节点 Pod 容量", + "xpack.metricsData.assetDetails.overview.kpi.subtitle.average": "平均值", "xpack.infra.assetDetails.header.return": "返回", "xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name", "xpack.infra.assetDetails.metadata.tooltip.metadata": "元数据", - "xpack.infra.assetDetails.metricsCharts.cpuUsage": "CPU 使用率", - "xpack.infra.assetDetails.metricsCharts.diskIOPS": "磁盘 IOPS", - "xpack.infra.assetDetails.metricsCharts.diskThroughput": "磁盘吞吐量", - "xpack.infra.assetDetails.metricsCharts.diskUsage": "磁盘使用率", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.available": "可用", - "xpack.infra.assetDetails.metricsCharts.diskUsage.label.used": "已使用", - "xpack.infra.assetDetails.metricsCharts.diskUsageByMountingPoint": "磁盘使用率(按装载点)", - "xpack.infra.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "节点 CPU 容量", - "xpack.infra.assetDetails.metricsCharts.load": "加载", - "xpack.infra.assetDetails.metricsCharts.logRate": "日志速率", - "xpack.infra.assetDetails.metricsCharts.memoryUsage": "内存利用率", - "xpack.infra.assetDetails.metricsCharts.metric.label.cache": "缓存", - "xpack.infra.assetDetails.metricsCharts.metric.label.free": "可用", - "xpack.infra.assetDetails.metricsCharts.metric.label.read": "读取", - "xpack.infra.assetDetails.metricsCharts.metric.label.used": "已使用", - "xpack.infra.assetDetails.metricsCharts.metric.label.write": "写入", - "xpack.infra.assetDetails.metricsCharts.network": "网络", - "xpack.infra.assetDetails.metricsCharts.network.label.rx": "入站 (RX)", - "xpack.infra.assetDetails.metricsCharts.network.label.tx": "出站 (TX)", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeDiskCapacity": "节点磁盘容量", - "xpack.infra.assetDetails.metricsCharts.nginx.nodeMemoryCapacity": "节点内存容量", - "xpack.infra.assetDetails.metricsCharts.nginx.nodePodCapacity": "节点 Pod 容量", - "xpack.infra.assetDetails.metricsCharts.normalizedLoad1m": "标准化负载", "xpack.infra.assetDetails.overview.alertsSectionTitle": "告警", "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Kubernetes 概览", "xpack.infra.assetDetails.overview.metadataSectionTitle": "元数据", @@ -20426,11 +20371,6 @@ "xpack.infra.assetDetailsEmbeddable.displayName": "资产详情", "xpack.infra.assetDetailsEmbeddable.metadataSummary.showAllMetadataButton": "全部显示", "xpack.infra.assetDetailsEmbeddable.notApplicableLabel": "不可用", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.cpuUsage.title": "CPU 使用率", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.diskUsage.title": "磁盘使用率", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.memoryUsage.title": "内存利用率", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.normalizedLoad1m.title": "标准化负载", - "xpack.infra.assetDetailsEmbeddable.overview.kpi.subtitle.average": "平均值", "xpack.infra.assetDetailsEmbeddable.overview.metadataCloudProviderHeading": "云服务提供商", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostIpHeading": "主机 IP", "xpack.infra.assetDetailsEmbeddable.overview.metadataHostOsVersionHeading": "主机操作系统版本", @@ -20548,19 +20488,7 @@ "xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "搜索日志条目......", "xpack.infra.hostsViewPage.tabs.logs.title": "日志", "xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "在 Lens 中打开", - "xpack.infra.hostsViewPage.tabs.metricsCharts.cpuUsage": "CPU 使用率", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIORead": "磁盘读取 IOPS", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskIOWrite": "磁盘写入 IOPS", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskReadThroughput": "磁盘读取吞吐量", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskSpaceAvailable": "可用磁盘空间", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskUsage": "磁盘使用率", - "xpack.infra.hostsViewPage.tabs.metricsCharts.diskWriteThroughput": "磁盘写入吞吐量", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryFree": "可用内存", - "xpack.infra.hostsViewPage.tabs.metricsCharts.memoryUsage": "内存利用率", - "xpack.infra.hostsViewPage.tabs.metricsCharts.normalizedLoad1m": "标准化负载", - "xpack.infra.hostsViewPage.tabs.metricsCharts.rx": "网络入站数据 (RX)", "xpack.infra.hostsViewPage.tabs.metricsCharts.title": "指标", - "xpack.infra.hostsViewPage.tabs.metricsCharts.tx": "网络出站数据 (TX)", "xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "这些指标是什么?", "xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "为什么我看到的是虚线?", "xpack.infra.infra.assetDetails.alerts.createAlertLink": "创建规则", @@ -22363,7 +22291,6 @@ "xpack.lens.config.configFlyoutCallout": "ES|QL 当前提供的配置选项数量有限", "xpack.lens.config.editLabel": "编辑配置", "xpack.lens.config.editLinkLabel": "在 Lens 中编辑", - "xpack.lens.config.editVisualizationLabel": "编辑可视化", "xpack.lens.config.experimentalLabel": "技术预览", "xpack.lens.configPanel.addLayerButton": "添加图层", "xpack.lens.configPanel.experimentalLabel": "技术预览", @@ -24373,7 +24300,6 @@ "xpack.ml.dataframe.analytics.create.analyticsProgressErrorMessage": "获取分析作业 {jobId} 的进度统计时发生错误", "xpack.ml.dataframe.analytics.create.configDetails.includedFieldsAndMoreDescription": "{includedFields}......(及另外 {extraCount} 个)", "xpack.ml.dataframe.analytics.create.createDataViewSuccessMessage": "已创建 Kibana 数据视图 {dataViewName}。", - "xpack.ml.dataframe.analytics.create.dataViewExistsError": "名为 {title} 的数据视图已存在。", "xpack.ml.dataframe.analytics.create.dependentVariableMaxDistictValuesError": "无效。{message}", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.description": "提供分析作业结果到 Kibana 仪表板、Discover 或其他网页的链接。{learnMoreLink}", "xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessageError": "数据视图 {dataViewName} 已存在。", @@ -25237,8 +25163,6 @@ "xpack.ml.dataframe.analytics.create.configDetails.standardizationEnabled": "标准化已启用", "xpack.ml.dataframe.analytics.create.configDetails.trainingPercent": "训练百分比", "xpack.ml.dataframe.analytics.create.createDataViewErrorMessage": "创建 Kibana 数据视图时发生错误:", - "xpack.ml.dataframe.analytics.create.createDataViewLabel": "创建数据视图", - "xpack.ml.dataframe.analytics.create.dataViewPermissionWarning": "您需要权限以创建数据视图。", "xpack.ml.dataframe.analytics.create.dependentVariableClassificationPlaceholder": "选择要预测的数值、类别或布尔值字段。", "xpack.ml.dataframe.analytics.create.dependentVariableInputAriaLabel": "输入要用作因变量的字段。", "xpack.ml.dataframe.analytics.create.dependentVariableLabel": "因变量", @@ -25246,18 +25170,11 @@ "xpack.ml.dataframe.analytics.create.dependentVariableOptionsNoNumericalFields": "没有为此数据视图找到任何数值类型字段。", "xpack.ml.dataframe.analytics.create.dependentVariableRegressionPlaceholder": "选择要预测的数值字段。", "xpack.ml.dataframe.analytics.create.destinationIndexHelpText": "已存在具有此名称的索引。请注意,运行此分析作业将会修改此目标索引。", - "xpack.ml.dataframe.analytics.create.destinationIndexInputAriaLabel": "选择唯一目标索引名称。", - "xpack.ml.dataframe.analytics.create.destinationIndexInvalidError": "目标索引名称无效。", - "xpack.ml.dataframe.analytics.create.destinationIndexLabel": "目标索引", "xpack.ml.dataframe.analytics.create.destinationIndexNotCreatedForDataFrameAnalyticsJob": "尚未创建目标索引。", - "xpack.ml.dataframe.analytics.create.DestIndexSameAsIdLabel": "目标索引与作业 ID 相同", "xpack.ml.dataframe.analytics.create.detailsDetails.editButtonText": "编辑", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrls.title": "定制 URL", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSection.customUrlsSelection.learnMoreLinkText": "了解详情", "xpack.ml.dataframe.analytics.create.detailsStep.additionalSectionButton": "其他设置", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldHelpText": "选择用于全局时间筛选的主要时间字段。", - "xpack.ml.dataframe.analytics.create.detailsStep.dataViewTimeFieldLabel": "Kibana 数据视图的时间字段", - "xpack.ml.dataframe.analytics.create.detailsStep.noTimeFieldOptionLabel": "我不想使用时间字段选项", "xpack.ml.dataframe.analytics.create.downsampleFactorInputAriaLabel": "用于为树训练计算损失函数导数的数据比例。", "xpack.ml.dataframe.analytics.create.downsampleFactorLabel": "降采样因子", "xpack.ml.dataframe.analytics.create.downsampleFactorText": "用于为树训练计算损失函数导数的数据比例。必须介于 0 和 1 之间。", @@ -25344,9 +25261,8 @@ "xpack.ml.dataFrame.analytics.create.searchSelection.CcsErrorCallOutTitle": "不支持使用跨集群搜索的数据视图。", "xpack.ml.dataFrame.analytics.create.searchSelection.errorGettingDataViewTitle": "加载已保存搜索所使用的数据视图时出错", "xpack.ml.dataFrame.analytics.create.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", - "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.indexPattern": "数据视图", + "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.dataView": "数据视图", "xpack.ml.dataFrame.analytics.create.searchSelection.savedObjectType.search": "已保存搜索", - "xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage": "如果没有为目标索引创建数据视图,则可能无法查看作业结果。", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitInputAriaLabel": "超过此深度的决策树将在损失计算中被罚分。", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitLabel": "软性树深度限制", "xpack.ml.dataframe.analytics.create.softTreeDepthLimitText": "超过此深度的决策树将在损失计算中被罚分。必须大于或等于 0。", @@ -25561,7 +25477,6 @@ "xpack.ml.dataframe.analyticsMap.noJobSelectedLabel": "未选择分析 ID", "xpack.ml.dataframe.analyticsMap.title": "用于分析的地图", "xpack.ml.dataframe.analyticsSourceSelection.title": "新分析作业/选择源数据视图", - "xpack.ml.dataframe.stepDetailsForm.destinationIndexInvalidErrorLink": "详细了解索引名称限制。", "xpack.ml.dataFrameAnalytics.analyticsMap.docTitle": "分析地图", "xpack.ml.dataFrameAnalytics.createJob.docTitle": "创建作业", "xpack.ml.dataFrameAnalytics.exploration.docTitle": "结果浏览器", @@ -29232,7 +29147,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.customEquationTooltip": "这支持基本数学 (A + B / C) 和布尔逻辑 (A < B ?A :B)。", "xpack.observability.customThreshold.rule.alertFlyout.dataViewError.noTimestamp": "选定数据视图没有时间戳字段,请选择其他数据视图。", "xpack.observability.customThreshold.rule.alertFlyout.defineTextQueryPrompt": "定义查询筛选(可选)", - "xpack.observability.customThreshold.rule.alertFlyout.error.aggregationRequired": "“聚合”必填。", "xpack.observability.customThreshold.rule.alertFlyout.error.equation.invalidCharacters": "方程字段仅支持以下字符:A-Z、+、-、/、*、(、)、?、!、&、:、|、>、<、=", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidFilterQuery": "筛选查询无效。", "xpack.observability.customThreshold.rule.alertFlyout.error.invalidSearchConfiguration": "需要数据视图。", @@ -32353,12 +32267,7 @@ "xpack.securitySolution.responseActionsList.list.recordRange": "正在显示第 {range} 个(共 {total} 个){recordsLabel}", "xpack.securitySolution.responseActionsList.list.recordRangeLabel": "{records, plural, other {响应操作}}", "xpack.securitySolution.riskInformation.howCalculatedText": "实体风险引擎每小时运行一次,以聚合过去 30 天出现的“未结”和“已确认”告警,并为主机或用户分配风险分数。然后,它会聚合各个风险分数并使用 {riemannZetaLink} 将其标准化为 0-100 范围。", - "xpack.securitySolution.riskInformation.learnMore": "详细了解 {riskEntity} 风险", "xpack.securitySolution.riskInformation.riskHeader": "{riskEntity} 风险分数范围", - "xpack.securitySolution.riskInformation.riskScoreFieldLabel": "{riskEntity} 风险分数", - "xpack.securitySolution.riskInformation.riskScoreFieldText": "{riskScoreField} 字段以单个数字值表示 {riskEntity} 的标准化风险。您可以在分类和响应 playbook 中将此值用作相对风险指标。", - "xpack.securitySolution.riskInformation.riskScoreLevelLabel": "{riskEntity} 风险级别", - "xpack.securitySolution.riskInformation.riskScoreLevelText": "{riskLevelField} 字段根据预定义的风险指标表示 {riskEntity} 的六个风险级别之一。", "xpack.securitySolution.riskScore.api.ingestPipeline.delete.errorMessageTitle": "无法删除采集{totalCount, plural, =1 {管道} other {管道}}", "xpack.securitySolution.riskScore.api.transforms.delete.errorMessageTitle": "无法删除{totalCount, plural, =1 {转换} other {转换}}", "xpack.securitySolution.riskScore.api.transforms.start.errorMessageTitle": "无法启动{totalCount, plural, =1 {转换} other {转换}}", @@ -36712,7 +36621,7 @@ "xpack.securitySolution.timeline.saveTimeline.modal.header": "保存时间线", "xpack.securitySolution.timeline.saveTimeline.modal.optionalLabel": "可选", "xpack.securitySolution.timeline.saveTimeline.modal.titleAriaLabel": "标题", - "xpack.securitySolution.timeline.saveTimeline.modal.titleTitle": "标题", + "xpack.securitySolution.timeline.saveTimeline.modal.title": "标题", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.discard.title": "丢弃时间线模板", "xpack.securitySolution.timeline.saveTimelineTemplate.modal.header": "保存时间线模板", "xpack.securitySolution.timeline.searchOrFilter.filterDescription": "上述数据提供程序的事件按相邻 KQL 进行筛选", @@ -40579,7 +40488,6 @@ "xpack.transform.stepCreateForm.createAlertRuleDescription": "打开向导以创建用于监测转换运行状况的告警规则。", "xpack.transform.stepCreateForm.createAndStartTransformButton": "创建并启动", "xpack.transform.stepCreateForm.createAndStartTransformDescription": "创建并启动转换。转换将增加集群的搜索和索引负荷。如果负荷超载,请停止转换。转换启动后,系统将为您提供继续浏览转换的选项。", - "xpack.transform.stepCreateForm.createDataViewLabel": "创建 Kibana 数据视图", "xpack.transform.stepCreateForm.createTransformButton": "创建", "xpack.transform.stepCreateForm.createTransformDescription": "在不启动转换的情况下创建转换。您之后能够通过返回到转换列表,来启动转换。", "xpack.transform.stepCreateForm.creatingDataViewMessage": "正在创建 Kibana 数据视图......", @@ -40665,15 +40573,7 @@ "xpack.transform.stepDetailsForm.continuousModeDelayLabel": "延迟", "xpack.transform.stepDetailsForm.continuousModeError": "连续模式不可用于没有日期字段的索引。", "xpack.transform.stepDetailsForm.createIndexAPI": "创建索引 API", - "xpack.transform.stepDetailsForm.dataViewPermissionWarning": "您需要权限以创建数据视图。", - "xpack.transform.stepDetailsForm.dataViewTimeFieldHelpText": "选择用于全局时间筛选的主要时间字段。", - "xpack.transform.stepDetailsForm.dataViewTimeFieldLabel": "Kibana 数据视图的时间字段", - "xpack.transform.stepDetailsForm.dataViewTitleError": "具有此名称的数据视图已存在。", "xpack.transform.stepDetailsForm.destinationIndexHelpText": "已存在具有此名称的索引。请注意,运行此转换将会修改此目标索引。", - "xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel": "选择唯一目标索引名称。", - "xpack.transform.stepDetailsForm.destinationIndexInvalidError": "目标索引名称无效。", - "xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink": "详细了解索引名称限制。", - "xpack.transform.stepDetailsForm.destinationIndexLabel": "目标索引", "xpack.transform.stepDetailsForm.destinationIngestPipelineAriaLabel": "选择采集管道(可选)", "xpack.transform.stepDetailsForm.destinationIngestPipelineComboBoxPlaceholder": "选择采集管道(可选)", "xpack.transform.stepDetailsForm.destinationIngestPipelineLabel": "目标采集管道", @@ -40692,7 +40592,6 @@ "xpack.transform.stepDetailsForm.maxPageSearchSizeHelpText": "用于每个检查点的组合聚合的初始页面大小。", "xpack.transform.stepDetailsForm.maxPageSearchSizeLabel": "最大页面搜索大小", "xpack.transform.stepDetailsForm.missingBucketCheckboxHelpTextLink": "了解详情", - "xpack.transform.stepDetailsForm.noTimeFieldOptionLabel": "我不想使用时间字段选项", "xpack.transform.stepDetailsForm.numFailureRetriesAriaLabel": "选择最大重试次数。", "xpack.transform.stepDetailsForm.NumFailureRetriesError": "重试次数需要介于 0 和 100 之间,或为 -1(表示无限重试)。", "xpack.transform.stepDetailsForm.retentionPolicyDateFieldHelpText": "选择可用于从目标索引中识别出日期文档的日期字段。", @@ -43027,7 +42926,6 @@ "cloud.deploymentDetails.cloudIDLabel": "云 ID", "cloud.deploymentDetails.createManageApiKeysButtonLabel": "创建和管理 API 密钥", "cloud.deploymentDetails.elasticEndpointLabel": "Elastic 终端", - "cloud.deploymentDetails.helpMenuLinks.endpoints": "终端", "cloud.deploymentDetails.learnMoreButtonLabel": "了解详情", "cloud.deploymentDetails.modal.closeButtonLabel": "关闭", "cloud.deploymentDetails.modal.learnMoreButtonLabel": "了解详情", @@ -43465,7 +43363,6 @@ "unifiedDocViewer.sourceViewer.errorMessageTitle": "发生错误", "unifiedDocViewer.sourceViewer.refresh": "刷新", "utils.filename.pathWarning": "路径的格式可能不正确;请验证值", - "utils.filename.wildcardWarning": "在文件路径中使用通配符可能会影响终端性能", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "在 Visualize 中启用仪表盘图表的旧版图表库。", "visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "仪表盘旧版图表库", "visTypeGauge.controls.gaugeOptions.alignmentLabel": "对齐方式", @@ -43618,7 +43515,6 @@ "xpack.cloudDataMigration.upgrade.text": "更轻松地升级到较新版本", "xpack.cloudLinks.deploymentLinkLabel": "管理此部署", "xpack.cloudLinks.helpMenuLinks.documentation": "文档", - "xpack.cloudLinks.helpMenuLinks.endpoints": "终端", "xpack.cloudLinks.helpMenuLinks.giveFeedback": "反馈", "xpack.cloudLinks.helpMenuLinks.support": "支持", "xpack.cloudLinks.setupGuide": "设置指南", @@ -43734,4 +43630,4 @@ "xpack.serverlessObservability.nav.projectSettings": "项目设置", "xpack.serverlessObservability.nav.visualizations": "可视化" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.ts deleted file mode 100644 index ef1bdee1d5490..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.ts +++ /dev/null @@ -1,111 +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 { AlertConsumers } from '@kbn/rule-data-utils'; -import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import { act, renderHook } from '@testing-library/react-hooks'; -import { useAlertDataView, UserAlertDataView } from './use_alert_data_view'; - -const mockUseKibanaReturnValue = createStartServicesMock(); - -jest.mock('@kbn/kibana-react-plugin/public', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); - -describe('useAlertDataView', () => { - const observabilityAlertFeatureIds: ValidFeatureId[] = [ - AlertConsumers.APM, - AlertConsumers.INFRASTRUCTURE, - AlertConsumers.LOGS, - AlertConsumers.UPTIME, - ]; - - beforeEach(() => { - mockUseKibanaReturnValue.http.get = jest.fn().mockReturnValue({ - index_name: [ - '.alerts-observability.uptime.alerts-*', - '.alerts-observability.metrics.alerts-*', - '.alerts-observability.logs.alerts-*', - '.alerts-observability.apm.alerts-*', - ], - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('initially is loading and does not have data', async () => { - await act(async () => { - const mockedAsyncDataView = { - loading: true, - error: undefined, - }; - - const { result, waitForNextUpdate } = renderHook(() => - useAlertDataView(observabilityAlertFeatureIds) - ); - - await waitForNextUpdate(); - - expect(result.current).toEqual(mockedAsyncDataView); - }); - }); - - it('returns dataView for the provided featureIds', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useAlertDataView(observabilityAlertFeatureIds) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toMatchInlineSnapshot(` - Object { - "error": undefined, - "loading": false, - "value": Array [ - Object { - "fieldFormatMap": Object {}, - "fields": Array [], - "title": ".alerts-observability.uptime.alerts-*,.alerts-observability.metrics.alerts-*,.alerts-observability.logs.alerts-*,.alerts-observability.apm.alerts-*", - }, - ], - } - `); - }); - }); - - it('returns error with no data when error happens', async () => { - const error = new Error('http error'); - mockUseKibanaReturnValue.http.get = jest.fn().mockImplementation(async () => { - throw error; - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useAlertDataView(observabilityAlertFeatureIds) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toMatchInlineSnapshot(` - Object { - "error": [Error: http error], - "loading": false, - "value": undefined, - } - `); - }); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx new file mode 100644 index 0000000000000..e37808a05d9b2 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx @@ -0,0 +1,162 @@ +/* + * Copyright 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 { AlertConsumers } from '@kbn/rule-data-utils'; +import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; +import type { ValidFeatureId } from '@kbn/rule-data-utils'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useAlertDataView } from './use_alert_data_view'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React from 'react'; + +const mockUseKibanaReturnValue = createStartServicesMock(); + +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + __esModule: true, + useKibana: jest.fn(() => ({ + services: mockUseKibanaReturnValue, + })), +})); + +jest.mock('../lib/rule_api/alert_index', () => ({ + fetchAlertIndexNames: jest.fn(), +})); + +const { fetchAlertIndexNames } = jest.requireMock('../lib/rule_api/alert_index'); + +jest.mock('../lib/rule_api/alert_fields', () => ({ + fetchAlertFields: jest.fn(), +})); +const { fetchAlertFields } = jest.requireMock('../lib/rule_api/alert_fields'); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, +}); +const wrapper = ({ children }: { children: Node }) => ( + {children} +); + +describe('useAlertDataView', () => { + const observabilityAlertFeatureIds: ValidFeatureId[] = [ + AlertConsumers.APM, + AlertConsumers.INFRASTRUCTURE, + AlertConsumers.LOGS, + AlertConsumers.UPTIME, + ]; + + beforeEach(() => { + fetchAlertIndexNames.mockResolvedValue([ + '.alerts-observability.uptime.alerts-*', + '.alerts-observability.metrics.alerts-*', + '.alerts-observability.logs.alerts-*', + '.alerts-observability.apm.alerts-*', + ]); + fetchAlertFields.mockResolvedValue([{ data: ' fields' }]); + }); + + afterEach(() => { + queryClient.clear(); + jest.clearAllMocks(); + }); + + it('initially is loading and does not have data', async () => { + await act(async () => { + const mockedAsyncDataView = { + loading: true, + dataview: undefined, + }; + + const { result, waitForNextUpdate } = renderHook( + () => useAlertDataView(observabilityAlertFeatureIds), + { + wrapper, + } + ); + + await waitForNextUpdate(); + + expect(result.current).toEqual(mockedAsyncDataView); + }); + }); + + it('fetch index names + fields for the provided o11y featureIds', async () => { + await act(async () => { + const { waitForNextUpdate } = renderHook( + () => useAlertDataView(observabilityAlertFeatureIds), + { + wrapper, + } + ); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1); + expect(fetchAlertFields).toHaveBeenCalledTimes(1); + }); + }); + + it('only fetch index names for security featureId', async () => { + await act(async () => { + const { waitForNextUpdate } = renderHook(() => useAlertDataView([AlertConsumers.SIEM]), { + wrapper, + }); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1); + expect(fetchAlertFields).toHaveBeenCalledTimes(0); + }); + }); + + it('Do not fetch anything if security and o11y featureIds are mix together', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => useAlertDataView([AlertConsumers.SIEM, AlertConsumers.LOGS]), + { + wrapper, + } + ); + + await waitForNextUpdate(); + + expect(fetchAlertIndexNames).toHaveBeenCalledTimes(0); + expect(fetchAlertFields).toHaveBeenCalledTimes(0); + expect(result.current).toEqual({ + loading: false, + dataview: undefined, + }); + }); + }); + + it('if fetch throw error return no data', async () => { + fetchAlertIndexNames.mockRejectedValue('error'); + + await act(async () => { + const { result, waitForNextUpdate } = renderHook( + () => useAlertDataView(observabilityAlertFeatureIds), + { + wrapper, + } + ); + + await waitForNextUpdate(); + await waitForNextUpdate(); + + expect(result.current).toEqual({ + loading: false, + dataview: undefined, + }); + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts index 15608192e7ddc..7b72e5898d56d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts @@ -5,72 +5,158 @@ * 2.0. */ -import { DataView, FieldSpec } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import { DataView } from '@kbn/data-views-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import useAsync from 'react-use/lib/useAsync'; -import { useMemo } from 'react'; +import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; +import { useEffect, useMemo, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { TriggersAndActionsUiServices } from '../..'; +import { fetchAlertIndexNames } from '../lib/rule_api/alert_index'; +import { fetchAlertFields } from '../lib/rule_api/alert_fields'; export interface UserAlertDataView { - value?: DataView[]; + dataviews?: DataView[]; loading: boolean; - error?: Error; } export function useAlertDataView(featureIds: ValidFeatureId[]): UserAlertDataView { - const { http } = useKibana().services; + const { + http, + data: dataService, + notifications: { toasts }, + } = useKibana().services; + const [dataviews, setDataviews] = useState(undefined); const features = featureIds.sort().join(','); + const isOnlySecurity = featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM); - const indexNames = useAsync(async () => { - const { index_name: indexNamesStr } = await http.get<{ index_name: string[] }>( - `${BASE_RAC_ALERTS_API_PATH}/index`, - { - query: { features }, - } - ); + const hasSecurityAndO11yFeatureIds = + featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - return indexNamesStr; - }, [features]); + const hasNoSecuritySolution = + featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; - const fields = useAsync(async () => { - const { fields: alertFields } = await http.get<{ fields: FieldSpec[] }>( - `${BASE_RAC_ALERTS_API_PATH}/browser_fields`, - { - query: { featureIds }, - } - ); - return alertFields; - }, [features]); + const queryIndexNameFn = () => { + return fetchAlertIndexNames({ http, features }); + }; - const dataview = useMemo( - () => - !fields.loading && - !indexNames.loading && - fields.error === undefined && - indexNames.error === undefined - ? ([ - { - title: (indexNames.value ?? []).join(','), - fieldFormatMap: {}, - fields: (fields.value ?? [])?.map((field) => { - return { - ...field, - ...(field.esTypes && field.esTypes.includes('flattened') - ? { type: 'string' } - : {}), - }; - }), - }, - ] as unknown as DataView[]) - : undefined, - [fields, indexNames] - ); + const queryAlertFieldsFn = () => { + return fetchAlertFields({ http, featureIds }); + }; - return { - value: dataview, - loading: fields.loading || indexNames.loading, - error: fields.error ? fields.error : indexNames.error, + const onErrorFn = () => { + toasts.addDanger( + i18n.translate('xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage', { + defaultMessage: 'Unable to load alert data view', + }) + ); }; + + const { + data: indexNames, + isSuccess: isIndexNameSuccess, + isInitialLoading: isIndexNameInitialLoading, + isLoading: isIndexNameLoading, + } = useQuery({ + queryKey: ['loadAlertIndexNames', features], + queryFn: queryIndexNameFn, + onError: onErrorFn, + refetchOnWindowFocus: false, + enabled: featureIds.length > 0 && !hasSecurityAndO11yFeatureIds, + }); + + const { + data: alertFields, + isSuccess: isAlertFieldsSuccess, + isInitialLoading: isAlertFieldsInitialLoading, + isLoading: isAlertFieldsLoading, + } = useQuery({ + queryKey: ['loadAlertFields', features], + queryFn: queryAlertFieldsFn, + onError: onErrorFn, + refetchOnWindowFocus: false, + enabled: hasNoSecuritySolution, + }); + + useEffect(() => { + return () => { + dataviews?.map((dv) => { + dataService.dataViews.clearInstanceCache(dv.id); + }); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dataviews]); + + // FUTURE ENGINEER this useEffect is for security solution user since + // we are using the user privilege to access the security alert index + useEffect(() => { + async function createDataView() { + const localDataview = await dataService.dataViews.create({ + title: (indexNames ?? []).join(','), + allowNoIndex: true, + }); + setDataviews([localDataview]); + } + + if (isOnlySecurity && isIndexNameSuccess) { + createDataView(); + } + }, [dataService.dataViews, indexNames, isIndexNameSuccess, isOnlySecurity]); + + // FUTURE ENGINEER this useEffect is for o11y and stack solution user since + // we are using the kibana user privilege to access the alert index + useEffect(() => { + if ( + indexNames && + alertFields && + !isOnlySecurity && + isAlertFieldsSuccess && + isIndexNameSuccess + ) { + setDataviews([ + { + title: (indexNames ?? []).join(','), + fieldFormatMap: {}, + fields: (alertFields ?? [])?.map((field) => { + return { + ...field, + ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), + }; + }), + }, + ] as unknown as DataView[]); + } + }, [ + alertFields, + dataService.dataViews, + indexNames, + isIndexNameSuccess, + isOnlySecurity, + isAlertFieldsSuccess, + ]); + + return useMemo( + () => ({ + dataviews, + loading: + featureIds.length === 0 || hasSecurityAndO11yFeatureIds + ? false + : isOnlySecurity + ? isIndexNameInitialLoading || isIndexNameLoading + : isIndexNameInitialLoading || + isIndexNameLoading || + isAlertFieldsInitialLoading || + isAlertFieldsLoading, + }), + [ + dataviews, + featureIds.length, + hasSecurityAndO11yFeatureIds, + isOnlySecurity, + isIndexNameInitialLoading, + isIndexNameLoading, + isAlertFieldsInitialLoading, + isAlertFieldsLoading, + ] + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts index 4892341e57385..ab11bb4f18452 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_load_rule_types_query.ts @@ -13,6 +13,7 @@ import { RuleType, RuleTypeIndex } from '../../types'; interface UseLoadRuleTypesQueryProps { filteredRuleTypes: string[]; + enabled?: boolean; } const getFilteredIndex = (data: Array>, filteredRuleTypes: string[]) => { @@ -32,7 +33,7 @@ const getFilteredIndex = (data: Array>, filteredRuleTyp }; export const useLoadRuleTypesQuery = (props: UseLoadRuleTypesQueryProps) => { - const { filteredRuleTypes } = props; + const { filteredRuleTypes, enabled = true } = props; const { http, notifications: { toasts }, @@ -55,6 +56,7 @@ export const useLoadRuleTypesQuery = (props: UseLoadRuleTypesQueryProps) => { queryFn, onError: onErrorFn, refetchOnWindowFocus: false, + enabled, }); const filteredIndex = data ? getFilteredIndex(data, filteredRuleTypes) : new Map(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts index 7fa2e3f0dfd04..1ad7106910113 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_fields.ts @@ -8,21 +8,67 @@ import { DataViewField } from '@kbn/data-views-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; -import useAsync from 'react-use/lib/useAsync'; -import type { AsyncState } from 'react-use/lib/useAsync'; +import { HttpSetup } from '@kbn/core/public'; +import { useQuery } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import { useMemo } from 'react'; import { TriggersAndActionsUiServices } from '../..'; -export function useRuleAADFields(ruleTypeId?: string): AsyncState { - const { http } = useKibana().services; +const EMPTY_AAD_FIELDS: DataViewField[] = []; - const aadFields = useAsync(async () => { - if (!ruleTypeId) return []; - const fields = await http.get(`${BASE_RAC_ALERTS_API_PATH}/aad_fields`, { - query: { ruleTypeId }, - }); +async function fetchAadFields({ + http, + ruleTypeId, +}: { + http: HttpSetup; + ruleTypeId?: string; +}): Promise { + if (!ruleTypeId) return EMPTY_AAD_FIELDS; + const fields = await http.get(`${BASE_RAC_ALERTS_API_PATH}/aad_fields`, { + query: { ruleTypeId }, + }); + + return fields; +} + +export function useRuleAADFields(ruleTypeId?: string): { + aadFields: DataViewField[]; + loading: boolean; +} { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const queryAadFieldsFn = () => { + return fetchAadFields({ http, ruleTypeId }); + }; + + const onErrorFn = () => { + toasts.addDanger( + i18n.translate('xpack.triggersActionsUI.useRuleAADFields.errorMessage', { + defaultMessage: 'Unable to load alert fields per rule type', + }) + ); + }; - return fields; + const { + data: aadFields = EMPTY_AAD_FIELDS, + isInitialLoading, + isLoading, + } = useQuery({ + queryKey: ['loadAlertAadFieldsPerRuleType', ruleTypeId], + queryFn: queryAadFieldsFn, + onError: onErrorFn, + refetchOnWindowFocus: false, + enabled: ruleTypeId !== undefined, }); - return aadFields; + return useMemo( + () => ({ + aadFields, + loading: ruleTypeId === undefined ? false : isInitialLoading || isLoading, + }), + [aadFields, isInitialLoading, isLoading, ruleTypeId] + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.ts new file mode 100644 index 0000000000000..7be5b3eec0e69 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_fields.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 { ValidFeatureId } from '@kbn/rule-data-utils'; +import { HttpSetup } from '@kbn/core/public'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; + +export async function fetchAlertFields({ + http, + featureIds, +}: { + http: HttpSetup; + featureIds: ValidFeatureId[]; +}): Promise { + const { fields: alertFields = [] } = await http.get<{ fields: FieldSpec[] }>( + `${BASE_RAC_ALERTS_API_PATH}/browser_fields`, + { + query: { featureIds }, + } + ); + return alertFields; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.ts new file mode 100644 index 0000000000000..8ac678664168b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/alert_index.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 { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; +import { HttpSetup } from '@kbn/core/public'; + +export async function fetchAlertIndexNames({ + http, + features, +}: { + http: HttpSetup; + features: string; +}): Promise { + const { index_name: indexNamesStr = [] } = await http.get<{ index_name: string[] }>( + `${BASE_RAC_ALERTS_API_PATH}/index`, + { + query: { features }, + } + ); + return indexNamesStr; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts index cc9b1ddab0218..54e987d18c917 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts @@ -75,7 +75,6 @@ describe('cloneRule', () => { "level": "info", "message": "alert ", }, - "useAlertDataForTemplate": undefined, "uuid": "123456", }, ], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts index da0cd8419e078..64949d9014c50 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts @@ -22,7 +22,7 @@ const transformAction: RewriteRequestCase = ({ id, params, actionTypeId, - useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' ? { useAlertDataForTemplate } : {}), ...(frequency ? { frequency: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index a2ab9c5b4dc3c..2304ee8c48930 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -38,7 +38,9 @@ const rewriteBodyRequest: RewriteResponseCase = ({ summary: frequency!.summary, }, alerts_filter: alertsFilter, - use_alert_data_for_template: useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), }) ), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 0d6371cfa1fdb..52158bfa2f034 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -28,7 +28,9 @@ const rewriteBodyRequest: RewriteResponseCase = ({ actions, ... summary: frequency!.summary, }, alerts_filter: alertsFilter, - use_alert_data_for_template: useAlertDataForTemplate, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), ...(uuid && { uuid }), }) ), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 83ad089c8e691..a9f49f609e13d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -8,7 +8,7 @@ import React, { Suspense, useEffect, useState, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ValidFeatureId, AlertConsumers } from '@kbn/rule-data-utils'; +import { ValidFeatureId } from '@kbn/rule-data-utils'; import { EuiFlexGroup, EuiFlexItem, @@ -428,8 +428,7 @@ export const ActionTypeForm = ({ setActionGroupIdByIndex && !actionItem.frequency?.summary; - const showActionAlertsFilter = - hasFieldsForAAD || producerId === AlertConsumers.SIEM || hasAlertsMappings; + const showActionAlertsFilter = hasFieldsForAAD; const accordionContent = checkEnabledResult.isEnabled ? ( <> 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 077be38b5616a..10e2a8493d711 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 @@ -9,12 +9,14 @@ import React, { useCallback, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { Query, TimeRange } from '@kbn/es-query'; import { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; +import { AlertConsumers } from '@kbn/rule-data-utils'; import { NO_INDEX_PATTERNS } from './constants'; import { SEARCH_BAR_PLACEHOLDER } from './translations'; import { AlertsSearchBarProps, QueryLanguageType } from './types'; import { useAlertDataView } from '../../hooks/use_alert_data_view'; import { TriggersAndActionsUiServices } from '../../..'; import { useRuleAADFields } from '../../hooks/use_rule_aad_fields'; +import { useLoadRuleTypesQuery } from '../../hooks/use_load_rule_types_query'; const SA_ALERTS = { type: 'alerts', fields: {} } as SuggestionsAbstraction; @@ -44,15 +46,22 @@ export function AlertsSearchBar({ } = useKibana().services; const [queryLanguage, setQueryLanguage] = useState('kuery'); - const { value: dataView, loading, error } = useAlertDataView(featureIds); - const { - value: aadFields, - loading: fieldsLoading, - error: fieldsError, - } = useRuleAADFields(ruleTypeId); + const { dataviews, loading } = useAlertDataView(featureIds ?? []); + const { aadFields, loading: fieldsLoading } = useRuleAADFields(ruleTypeId); const indexPatterns = - ruleTypeId && aadFields?.length ? [{ title: ruleTypeId, fields: aadFields }] : dataView; + ruleTypeId && aadFields?.length ? [{ title: ruleTypeId, fields: aadFields }] : dataviews; + + const ruleType = useLoadRuleTypesQuery({ + filteredRuleTypes: ruleTypeId !== undefined ? [ruleTypeId] : [], + enabled: ruleTypeId !== undefined, + }); + + const isSecurity = + (featureIds && featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM)) || + (ruleType && + ruleTypeId && + ruleType.ruleTypesState.data.get(ruleTypeId)?.producer === AlertConsumers.SIEM); const onSearchQuerySubmit = useCallback( ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { @@ -86,9 +95,7 @@ export function AlertsSearchBar({ appName={appName} disableQueryLanguageSwitcher={disableQueryLanguageSwitcher} // @ts-expect-error - DataView fields prop and SearchBar indexPatterns props are overly broad - indexPatterns={ - loading || error || fieldsLoading || fieldsError ? NO_INDEX_PATTERNS : indexPatterns - } + indexPatterns={loading || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns} placeholder={placeholder} query={{ query: query ?? '', language: queryLanguage }} filters={filters} @@ -105,7 +112,7 @@ export function AlertsSearchBar({ showSubmitButton={showSubmitButton} submitOnBlur={submitOnBlur} onQueryChange={onSearchQueryChange} - suggestionsAbstraction={SA_ALERTS} + suggestionsAbstraction={isSecurity ? undefined : SA_ALERTS} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx index 7f6a45c7d9992..45fbd63a635a8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx @@ -313,6 +313,7 @@ const RuleAdd = ({ hideGrouping={hideGrouping} hideInterval={hideInterval} onChangeMetaData={onChangeMetaData} + selectedConsumer={selectedConsumer} setConsumer={setSelectedConsumer} useRuleProducer={useRuleProducer} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx index 9d21970f42a08..756d2edf10790 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.test.tsx @@ -262,6 +262,7 @@ describe('rule_form', () => { ruleTypesOverwrite?: RuleType[]; ruleTypeModelOverwrite?: RuleTypeModel; useRuleProducer?: boolean; + selectedConsumer?: RuleCreationValidConsumer | null; }) { const { showRulesList = false, @@ -273,6 +274,7 @@ describe('rule_form', () => { ruleTypesOverwrite, ruleTypeModelOverwrite, useRuleProducer = false, + selectedConsumer, } = options || {}; const mocks = coreMock.createSetup(); @@ -325,7 +327,11 @@ describe('rule_form', () => { enabledInLicense: false, }, ]; - useLoadRuleTypes.mockReturnValue({ ruleTypes }); + const ruleTypeIndex = ruleTypes.reduce((acc, item) => { + acc.set(item.id, item); + return acc; + }, new Map()); + useLoadRuleTypes.mockReturnValue({ ruleTypes, ruleTypeIndex }); const [ { application: { capabilities }, @@ -377,7 +383,7 @@ describe('rule_form', () => { minimumScheduleInterval: { value: '1m', enforce: enforceMinimum }, }} dispatch={() => {}} - errors={{ name: [], 'schedule.interval': [], ruleTypeId: [] }} + errors={{ name: [], 'schedule.interval': [], ruleTypeId: [], actionConnectors: [] }} operation="create" actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} @@ -386,6 +392,7 @@ describe('rule_form', () => { validConsumers={validConsumers} setConsumer={mockSetConsumer} useRuleProducer={useRuleProducer} + selectedConsumer={selectedConsumer} /> ); @@ -666,6 +673,361 @@ describe('rule_form', () => { expect(wrapper.find('[data-test-subj="ruleFormConsumerSelect"]').exists()).toBeFalsy(); }); + + it('Do not show alert query in action when multi consumer rule type does not have a consumer selected', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'alerts', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: true, + hasAlertsMappings: true, + }, + ], + ruleTypeModelOverwrite: { + id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(false); + }); + + it('Do not show alert query in action when we do not have hasFieldsForAAD or hasAlertsMappings or belong to security', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'alerts', + ruleTypeId: 'my-rule-type', + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: 'my-rule-type', + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: false, + hasAlertsMappings: false, + }, + ], + ruleTypeModelOverwrite: { + id: 'my-rule-type', + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(false); + }); + + it('Show alert query in action when rule type hasFieldsForAAD', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'alerts', + ruleTypeId: 'my-rule-type', + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: 'my-rule-type', + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: true, + hasAlertsMappings: false, + }, + ], + ruleTypeModelOverwrite: { + id: 'my-rule-type', + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(true); + }); + + it('Show alert query in action when rule type hasAlertsMappings', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'alerts', + ruleTypeId: 'my-rule-type', + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: 'my-rule-type', + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: false, + hasAlertsMappings: true, + }, + ], + ruleTypeModelOverwrite: { + id: 'my-rule-type', + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(true); + }); + + it('Show alert query in action when rule type is from security solution', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'siem', + ruleTypeId: 'my-rule-type', + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: 'my-rule-type', + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: 'siem', + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: false, + hasAlertsMappings: false, + }, + ], + ruleTypeModelOverwrite: { + id: 'my-rule-type', + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(true); + }); + + it('show alert query in action when multi consumer rule type does not have a consumer selected', async () => { + await setup({ + initialRuleOverwrite: { + name: 'Simple rule', + consumer: 'alerts', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + schedule: { + interval: '1h', + }, + }, + ruleTypesOverwrite: [ + { + id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + name: 'Threshold Rule', + actionGroups: [ + { + id: 'testActionGroup', + name: 'Test Action Group', + }, + ], + enabledInLicense: true, + defaultActionGroupId: 'threshold.fired', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, + producer: ALERTS_FEATURE_ID, + authorizedConsumers: { + infrastructure: { read: true, all: true }, + logs: { read: true, all: true }, + }, + actionVariables: { + context: [], + state: [], + params: [], + }, + hasFieldsForAAD: true, + hasAlertsMappings: true, + }, + ], + ruleTypeModelOverwrite: { + id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + iconClass: 'test', + description: 'test', + documentationUrl: null, + validate: (): ValidationResult => { + return { errors: {} }; + }, + ruleParamsExpression: TestExpression, + requiresAppContext: false, + }, + selectedConsumer: 'logs', + }); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(true); + }); }); describe('rule_form create rule non ruleing consumer and producer', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 5ee7ef7f2244a..174e4c5ebb52e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -154,6 +154,7 @@ interface RuleFormProps> { hideGrouping?: boolean; hideInterval?: boolean; connectorFeatureId?: string; + selectedConsumer?: RuleCreationValidConsumer | null; validConsumers?: RuleCreationValidConsumer[]; onChangeMetaData: (metadata: MetaData) => void; useRuleProducer?: boolean; @@ -178,6 +179,7 @@ export const RuleForm = ({ hideGrouping = false, hideInterval, connectorFeatureId = AlertingConnectorFeatureId, + selectedConsumer, validConsumers, onChangeMetaData, useRuleProducer, @@ -646,6 +648,23 @@ export const RuleForm = ({ } }; + const hasFieldsForAAD = useMemo(() => { + const hasAlertHasData = selectedRuleType + ? selectedRuleType.hasFieldsForAAD || + selectedRuleType.producer === AlertConsumers.SIEM || + selectedRuleType.hasAlertsMappings + : false; + + if (MULTI_CONSUMER_RULE_TYPE_IDS.includes(rule?.ruleTypeId ?? '')) { + return ( + (validConsumers || VALID_CONSUMERS).includes( + selectedConsumer as RuleCreationValidConsumer + ) && hasAlertHasData + ); + } + return hasAlertHasData; + }, [rule?.ruleTypeId, selectedConsumer, selectedRuleType, validConsumers]); + const ruleTypeDetails = ( <> @@ -824,8 +843,12 @@ export const RuleForm = ({ defaultActionGroupId={defaultActionGroupId} hasAlertsMappings={selectedRuleType.hasAlertsMappings} featureId={connectorFeatureId} - producerId={selectedRuleType.producer} - hasFieldsForAAD={selectedRuleType.hasFieldsForAAD} + producerId={ + MULTI_CONSUMER_RULE_TYPE_IDS.includes(rule.ruleTypeId) + ? selectedConsumer ?? rule.consumer + : selectedRuleType.producer + } + hasFieldsForAAD={hasFieldsForAAD} ruleTypeId={rule.ruleTypeId} isActionGroupDisabledForActionType={(actionGroupId: string, actionTypeId: string) => isActionGroupDisabledForActionType(selectedRuleType, actionGroupId, actionTypeId) @@ -901,8 +924,16 @@ export const RuleForm = ({ + + + } > = lazy( () => import('../application/sections/alerts_search_bar/alerts_search_bar') ); +const queryClient = new QueryClient(); + export const getAlertsSearchBarLazy = (props: AlertsSearchBarProps) => ( }> - + + + ); diff --git a/x-pack/test/accessibility/apps/advanced_settings.ts b/x-pack/test/accessibility/apps/group1/advanced_settings.ts similarity index 97% rename from x-pack/test/accessibility/apps/advanced_settings.ts rename to x-pack/test/accessibility/apps/group1/advanced_settings.ts index 6c931f0a0e5a1..44899932302ba 100644 --- a/x-pack/test/accessibility/apps/advanced_settings.ts +++ b/x-pack/test/accessibility/apps/group1/advanced_settings.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'settings', 'header']); diff --git a/x-pack/test/accessibility/apps/group1/config.ts b/x-pack/test/accessibility/apps/group1/config.ts new file mode 100644 index 0000000000000..8e5510141abf9 --- /dev/null +++ b/x-pack/test/accessibility/apps/group1/config.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services } from '../../services'; +import { pageObjects } from '../../page_objects'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../functional/config.base.js') + ); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('.')], + + pageObjects, + services, + + junit: { + reportName: 'X-Pack Accessibility Tests - Group 1', + }, + }; +} diff --git a/x-pack/test/accessibility/apps/dashboard_controls.ts b/x-pack/test/accessibility/apps/group1/dashboard_controls.ts similarity index 98% rename from x-pack/test/accessibility/apps/dashboard_controls.ts rename to x-pack/test/accessibility/apps/group1/dashboard_controls.ts index 74f7288ccce9e..dd45b68674343 100644 --- a/x-pack/test/accessibility/apps/dashboard_controls.ts +++ b/x-pack/test/accessibility/apps/group1/dashboard_controls.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/dashboard_panel_options.ts b/x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts similarity index 97% rename from x-pack/test/accessibility/apps/dashboard_panel_options.ts rename to x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts index 4e4dc3b218d79..5f12f3600c29a 100644 --- a/x-pack/test/accessibility/apps/dashboard_panel_options.ts +++ b/x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; -import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import type { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/grok_debugger.ts b/x-pack/test/accessibility/apps/group1/grok_debugger.ts similarity index 94% rename from x-pack/test/accessibility/apps/grok_debugger.ts rename to x-pack/test/accessibility/apps/group1/grok_debugger.ts index 8ee9114c7da0a..da630c6bed7b3 100644 --- a/x-pack/test/accessibility/apps/grok_debugger.ts +++ b/x-pack/test/accessibility/apps/group1/grok_debugger.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'security']); diff --git a/x-pack/test/accessibility/apps/helpers.ts b/x-pack/test/accessibility/apps/group1/helpers.ts similarity index 100% rename from x-pack/test/accessibility/apps/helpers.ts rename to x-pack/test/accessibility/apps/group1/helpers.ts diff --git a/x-pack/test/accessibility/apps/home.ts b/x-pack/test/accessibility/apps/group1/home.ts similarity index 97% rename from x-pack/test/accessibility/apps/home.ts rename to x-pack/test/accessibility/apps/group1/home.ts index 544a32843f7f3..800312bb4de5f 100644 --- a/x-pack/test/accessibility/apps/home.ts +++ b/x-pack/test/accessibility/apps/group1/home.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const { common, home } = getPageObjects(['common', 'home']); diff --git a/x-pack/test/accessibility/apps/group1/index.ts b/x-pack/test/accessibility/apps/group1/index.ts new file mode 100644 index 0000000000000..d8cd2e76c42dd --- /dev/null +++ b/x-pack/test/accessibility/apps/group1/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('X-Pack Accessibility Tests - Group 1', function () { + loadTestFile(require.resolve('./login_page')); + loadTestFile(require.resolve('./kibana_overview')); + loadTestFile(require.resolve('./home')); + loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./grok_debugger')); + loadTestFile(require.resolve('./search_profiler')); + loadTestFile(require.resolve('./painless_lab')); + loadTestFile(require.resolve('./uptime')); + loadTestFile(require.resolve('./spaces')); + loadTestFile(require.resolve('./advanced_settings')); + loadTestFile(require.resolve('./dashboard_panel_options')); + loadTestFile(require.resolve('./dashboard_controls')); + loadTestFile(require.resolve('./users')); + loadTestFile(require.resolve('./roles')); + loadTestFile(require.resolve('./ingest_node_pipelines')); + loadTestFile(require.resolve('./index_lifecycle_management')); + }); +}; diff --git a/x-pack/test/accessibility/apps/index_lifecycle_management.ts b/x-pack/test/accessibility/apps/group1/index_lifecycle_management.ts similarity index 98% rename from x-pack/test/accessibility/apps/index_lifecycle_management.ts rename to x-pack/test/accessibility/apps/group1/index_lifecycle_management.ts index fc3ec1ff5cf81..b994c4193e49f 100644 --- a/x-pack/test/accessibility/apps/index_lifecycle_management.ts +++ b/x-pack/test/accessibility/apps/group1/index_lifecycle_management.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; const REPO_NAME = 'test'; const POLICY_NAME = 'ilm-a11y-test'; diff --git a/x-pack/test/accessibility/apps/ingest_node_pipelines.ts b/x-pack/test/accessibility/apps/group1/ingest_node_pipelines.ts similarity index 100% rename from x-pack/test/accessibility/apps/ingest_node_pipelines.ts rename to x-pack/test/accessibility/apps/group1/ingest_node_pipelines.ts diff --git a/x-pack/test/accessibility/apps/kibana_overview.ts b/x-pack/test/accessibility/apps/group1/kibana_overview.ts similarity index 93% rename from x-pack/test/accessibility/apps/kibana_overview.ts rename to x-pack/test/accessibility/apps/group1/kibana_overview.ts index 373044c4bffc3..fe1bc55cd4c00 100644 --- a/x-pack/test/accessibility/apps/kibana_overview.ts +++ b/x-pack/test/accessibility/apps/group1/kibana_overview.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home']); diff --git a/x-pack/test/accessibility/apps/login_page.ts b/x-pack/test/accessibility/apps/group1/login_page.ts similarity index 97% rename from x-pack/test/accessibility/apps/login_page.ts rename to x-pack/test/accessibility/apps/group1/login_page.ts index 3993d9ffcd72e..32cb825f86b33 100644 --- a/x-pack/test/accessibility/apps/login_page.ts +++ b/x-pack/test/accessibility/apps/group1/login_page.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/management.ts b/x-pack/test/accessibility/apps/group1/management.ts similarity index 97% rename from x-pack/test/accessibility/apps/management.ts rename to x-pack/test/accessibility/apps/group1/management.ts index 2021642c2aa27..82c7baf8e830d 100644 --- a/x-pack/test/accessibility/apps/management.ts +++ b/x-pack/test/accessibility/apps/group1/management.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ diff --git a/x-pack/test/accessibility/apps/painless_lab.ts b/x-pack/test/accessibility/apps/group1/painless_lab.ts similarity index 97% rename from x-pack/test/accessibility/apps/painless_lab.ts rename to x-pack/test/accessibility/apps/group1/painless_lab.ts index a0a4712dbe4e3..522ffa9c7b238 100644 --- a/x-pack/test/accessibility/apps/painless_lab.ts +++ b/x-pack/test/accessibility/apps/group1/painless_lab.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'security']); diff --git a/x-pack/test/accessibility/apps/roles.ts b/x-pack/test/accessibility/apps/group1/roles.ts similarity index 98% rename from x-pack/test/accessibility/apps/roles.ts rename to x-pack/test/accessibility/apps/group1/roles.ts index 5369dced427fa..cf798bcb853f5 100644 --- a/x-pack/test/accessibility/apps/roles.ts +++ b/x-pack/test/accessibility/apps/group1/roles.ts @@ -7,7 +7,7 @@ // a11y tests for spaces, space selection and spacce creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['security', 'settings']); diff --git a/x-pack/test/accessibility/apps/search_profiler.ts b/x-pack/test/accessibility/apps/group1/search_profiler.ts similarity index 97% rename from x-pack/test/accessibility/apps/search_profiler.ts rename to x-pack/test/accessibility/apps/group1/search_profiler.ts index 30043f8f4157f..522c5e4cf730e 100644 --- a/x-pack/test/accessibility/apps/search_profiler.ts +++ b/x-pack/test/accessibility/apps/group1/search_profiler.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'security']); diff --git a/x-pack/test/accessibility/apps/spaces.ts b/x-pack/test/accessibility/apps/group1/spaces.ts similarity index 98% rename from x-pack/test/accessibility/apps/spaces.ts rename to x-pack/test/accessibility/apps/group1/spaces.ts index 622b1b3cefd64..33616c3576b1d 100644 --- a/x-pack/test/accessibility/apps/spaces.ts +++ b/x-pack/test/accessibility/apps/group1/spaces.ts @@ -7,7 +7,7 @@ // a11y tests for spaces, space selection and space creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'spaceSelector', 'home', 'header', 'security']); diff --git a/x-pack/test/accessibility/apps/uptime.ts b/x-pack/test/accessibility/apps/group1/uptime.ts similarity index 91% rename from x-pack/test/accessibility/apps/uptime.ts rename to x-pack/test/accessibility/apps/group1/uptime.ts index 49243c37fe730..3818ddb1061a2 100644 --- a/x-pack/test/accessibility/apps/uptime.ts +++ b/x-pack/test/accessibility/apps/group1/uptime.ts @@ -6,8 +6,8 @@ */ import moment from 'moment'; -import { FtrProviderContext } from '../ftr_provider_context'; -import { makeChecks } from '../../api_integration/apis/uptime/rest/helper/make_checks'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { makeChecks } from '../../../api_integration/apis/uptime/rest/helper/make_checks'; const A11Y_TEST_MONITOR_ID = 'a11yTestMonitor'; @@ -19,7 +19,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const toasts = getService('toasts'); - describe('uptime Accessibility', () => { + // github.com/elastic/kibana/issues/153601 + describe.skip('uptime Accessibility', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/uptime/blank'); await makeChecks(es, A11Y_TEST_MONITOR_ID, 150, 1, 1000, { diff --git a/x-pack/test/accessibility/apps/users.ts b/x-pack/test/accessibility/apps/group1/users.ts similarity index 98% rename from x-pack/test/accessibility/apps/users.ts rename to x-pack/test/accessibility/apps/group1/users.ts index 6057b4d45bb09..e26e6a6f6a54f 100644 --- a/x-pack/test/accessibility/apps/users.ts +++ b/x-pack/test/accessibility/apps/group1/users.ts @@ -7,7 +7,7 @@ // a11y tests for spaces, space selection and spacce creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['security', 'settings']); diff --git a/x-pack/test/accessibility/apps/group2/config.ts b/x-pack/test/accessibility/apps/group2/config.ts new file mode 100644 index 0000000000000..27cf620bc05c8 --- /dev/null +++ b/x-pack/test/accessibility/apps/group2/config.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services } from '../../services'; +import { pageObjects } from '../../page_objects'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../functional/config.base.js') + ); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('.')], + + pageObjects, + services, + + junit: { + reportName: 'X-Pack Accessibility Tests - Group 2', + }, + }; +} diff --git a/x-pack/test/accessibility/apps/group2/index.ts b/x-pack/test/accessibility/apps/group2/index.ts new file mode 100644 index 0000000000000..2c6bf4e58a08b --- /dev/null +++ b/x-pack/test/accessibility/apps/group2/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('X-Pack Accessibility Tests - Group 2', function () { + loadTestFile(require.resolve('./ml')); + loadTestFile(require.resolve('./ml_anomaly_detection')); + loadTestFile(require.resolve('./transform')); + loadTestFile(require.resolve('./lens')); + }); +}; diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/group2/lens.ts similarity index 98% rename from x-pack/test/accessibility/apps/lens.ts rename to x-pack/test/accessibility/apps/group2/lens.ts index 1153d61d1fc68..860138fc77701 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/group2/lens.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'home', 'lens']); @@ -25,11 +25,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { - await PageObjects.common.navigateToApp('visualize'); - await listingTable.searchForItemWithName(lensChartName); - await listingTable.checkListingSelectAllCheckbox(); - await listingTable.clickDeleteSelected(); - await PageObjects.common.clickConfirmOnModal(); await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' @@ -173,6 +168,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('saves lens chart', async () => { await PageObjects.lens.save(lensChartName); await a11y.testAppSnapshot(); + // delete newly created Lens + await PageObjects.common.navigateToApp('visualize'); + await listingTable.searchForItemWithName(lensChartName); + await listingTable.checkListingSelectAllCheckbox(); + await listingTable.clickDeleteSelected(); + await PageObjects.common.clickConfirmOnModal(); }); }); } diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/group2/ml.ts similarity index 92% rename from x-pack/test/accessibility/apps/ml.ts rename to x-pack/test/accessibility/apps/group2/ml.ts index dd2fad6de05de..af9664b5e258a 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/group2/ml.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); @@ -50,10 +50,10 @@ export default function ({ getService }: FtrProviderContext) { describe('with data loaded', function () { const dfaOutlierResultsJobId = 'iph_outlier_a11y'; - const ecIndexPattern = 'ft_module_sample_ecommerce'; - const ihpIndexPattern = 'ft_ihp_outlier'; - const egsIndexPattern = 'ft_egs_regression'; - const bmIndexPattern = 'ft_bank_marketing'; + const ecIndexName = 'ft_module_sample_ecommerce'; + const ihpIndexName = 'ft_ihp_outlier'; + const egsIndexName = 'ft_egs_regression'; + const bmIndexName = 'ft_bank_marketing'; const ecExpectedTotalCount = '287'; const dfaOutlierJobType = 'outlier_detection'; @@ -68,7 +68,7 @@ export default function ({ getService }: FtrProviderContext) { const dfaClassificationJobTrainingPercent = 30; const uploadFilePath = require.resolve( - '../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log' + '../../../functional/apps/ml/data_visualizer/files_to_import/artificial_server_log' ); before(async () => { @@ -78,10 +78,10 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/module_sample_ecommerce' ); - await ml.testResources.createIndexPatternIfNeeded(ihpIndexPattern); - await ml.testResources.createIndexPatternIfNeeded(egsIndexPattern); - await ml.testResources.createIndexPatternIfNeeded(bmIndexPattern); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.createDataViewIfNeeded(ihpIndexName); + await ml.testResources.createDataViewIfNeeded(egsIndexName); + await ml.testResources.createDataViewIfNeeded(bmIndexName); + await ml.testResources.createDataViewIfNeeded(ecIndexName, 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunDFAJob( @@ -93,10 +93,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.cleanMlIndices(); await ml.api.deleteIndices(`user-${dfaOutlierResultsJobId}`); - await ml.testResources.deleteIndexPatternByTitle(ihpIndexPattern); - await ml.testResources.deleteIndexPatternByTitle(egsIndexPattern); - await ml.testResources.deleteIndexPatternByTitle(bmIndexPattern); - await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern); + await ml.testResources.deleteDataViewByTitle(ihpIndexName); + await ml.testResources.deleteDataViewByTitle(egsIndexName); + await ml.testResources.deleteDataViewByTitle(bmIndexName); + await ml.testResources.deleteDataViewByTitle(ecIndexName); await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/egs_regression'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/bm_classification'); @@ -134,7 +134,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'job creation selects the source data and loads the DFA job wizard page' ); - await ml.jobSourceSelection.selectSourceForAnalyticsJob(ihpIndexPattern); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(ihpIndexName); await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); await a11y.testAppSnapshot(); }); @@ -180,7 +180,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToMl(); await ml.navigation.navigateToDataFrameAnalytics(); await ml.dataFrameAnalytics.startAnalyticsCreation(); - await ml.jobSourceSelection.selectSourceForAnalyticsJob(egsIndexPattern); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(egsIndexName); await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); await ml.testExecution.logTestStep('selects the regression job type'); await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); @@ -227,7 +227,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToMl(); await ml.navigation.navigateToDataFrameAnalytics(); await ml.dataFrameAnalytics.startAnalyticsCreation(); - await ml.jobSourceSelection.selectSourceForAnalyticsJob(bmIndexPattern); + await ml.jobSourceSelection.selectSourceForAnalyticsJob(bmIndexName); await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive(); await ml.testExecution.logTestStep('selects the classification job type'); await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists(); @@ -275,12 +275,12 @@ export default function ({ getService }: FtrProviderContext) { }); it('index data visualizer select index pattern page', async () => { - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await a11y.testAppSnapshot(); }); it('index data visualizer page for selected index', async () => { - await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); + await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexName); await ml.testExecution.logTestStep('should display the time range step'); await ml.dataVisualizerIndexBased.assertTimeRangeSelectorSectionExists(); diff --git a/x-pack/test/accessibility/apps/ml_anomaly_detection.ts b/x-pack/test/accessibility/apps/group2/ml_anomaly_detection.ts similarity index 95% rename from x-pack/test/accessibility/apps/ml_anomaly_detection.ts rename to x-pack/test/accessibility/apps/group2/ml_anomaly_detection.ts index c8cff8616f23c..33c7db9eb0b20 100644 --- a/x-pack/test/accessibility/apps/ml_anomaly_detection.ts +++ b/x-pack/test/accessibility/apps/group2/ml_anomaly_detection.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; interface Detector { identifier: string; @@ -65,10 +65,10 @@ export default function ({ getService }: FtrProviderContext) { const eventDescription = 'calendar_event_a11y'; const filterId = 'filter_a11y'; const filterItems = ['filter_item_a11y']; - const fqIndexPattern = 'ft_farequote'; - const ecIndexPattern = 'ft_module_sample_ecommerce'; + const fqIndexName = 'ft_farequote'; + const ecIndexName = 'ft_module_sample_ecommerce'; - const categorizationIndexPattern = 'ft_categorization_small'; + const categorizationIndexName = 'ft_categorization_small'; const adJobAggAndFieldIdentifier = 'Mean(responsetime)'; const adJobBucketSpan = '30m'; @@ -87,12 +87,12 @@ export default function ({ getService }: FtrProviderContext) { const advancedJobTestData = { suiteTitle: 'with multiple metric detectors and custom datafeed settings', - jobSource: ecIndexPattern, + jobSource: ecIndexName, jobId: `ec_advanced_1_${Date.now()}`, get jobIdClone(): string { return `${this.jobId}_clone`; }, - jobDescription: `Create advanced job from ${ecIndexPattern} dataset with multiple metric detectors and custom datafeed settings`, + jobDescription: `Create advanced job from ${ecIndexName} dataset with multiple metric detectors and custom datafeed settings`, jobGroups: ['automated', 'ecommerce', 'advanced'], get jobGroupsClone(): string[] { return [...this.jobGroups, 'clone']; @@ -149,7 +149,7 @@ export default function ({ getService }: FtrProviderContext) { }; const populationJobTestData = { suiteTitle: 'population job', - jobSource: ecIndexPattern, + jobSource: ecIndexName, jobId: `ec_population_1_${Date.now()}`, get jobIdClone(): string { return `${this.jobId}_clone`; @@ -201,12 +201,9 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/categorization_small' ); - await ml.testResources.createIndexPatternIfNeeded(fqIndexPattern, '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); - await ml.testResources.createIndexPatternIfNeeded( - 'ft_categorization_small', - '@timestamp' - ); + await ml.testResources.createDataViewIfNeeded(fqIndexName, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(ecIndexName, 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_categorization_small', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob( @@ -238,9 +235,9 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.deleteCalendar(calendarId); await ml.api.deleteFilter(filterId); - await ml.testResources.deleteIndexPatternByTitle(fqIndexPattern); - await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern); - await ml.testResources.deleteIndexPatternByTitle(categorizationIndexPattern); + await ml.testResources.deleteDataViewByTitle(fqIndexName); + await ml.testResources.deleteDataViewByTitle(ecIndexName); + await ml.testResources.deleteDataViewByTitle(categorizationIndexName); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/categorization_small'); @@ -259,7 +256,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('anomaly detection create job select type page', async () => { - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexName); await a11y.testAppSnapshot(); }); @@ -308,7 +305,7 @@ export default function ({ getService }: FtrProviderContext) { // as the other steps have already been tested for the single metric job await ml.navigation.navigateToAnomalyDetection(); await ml.jobManagement.navigateToNewJobSourceSelection(); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexPattern); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(fqIndexName); await ml.jobTypeSelection.selectMultiMetricJob(); await ml.testExecution.logTestStep('job creation set the time range'); await ml.jobWizardCommon.clickUseFullDataButton( @@ -442,7 +439,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToJobManagement(); await ml.jobManagement.navigateToNewJobSourceSelection(); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexPattern); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexName); await ml.testExecution.logTestStep('job creation loads the population job wizard page'); await ml.jobTypeSelection.selectPopulationJob(); @@ -509,9 +506,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToJobManagement(); await ml.jobManagement.navigateToNewJobSourceSelection(); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob( - categorizationIndexPattern - ); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(categorizationIndexName); await ml.testExecution.logTestStep( 'job creation loads the categorization job wizard page' @@ -566,7 +561,7 @@ export default function ({ getService }: FtrProviderContext) { it('anomaly detection create job from data recognizer module open wizard', async () => { await ml.navigation.navigateToJobManagement(); await ml.jobManagement.navigateToNewJobSourceSelection(); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexPattern); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(ecIndexName); await ml.testExecution.logTestStep( `job creation loads the data recognizer job wizard page for the ${adRecognizerJobModuleId} module` ); diff --git a/x-pack/test/accessibility/apps/transform.ts b/x-pack/test/accessibility/apps/group2/transform.ts similarity index 87% rename from x-pack/test/accessibility/apps/transform.ts rename to x-pack/test/accessibility/apps/group2/transform.ts index 9d52571956816..dac3f8545001b 100644 --- a/x-pack/test/accessibility/apps/transform.ts +++ b/x-pack/test/accessibility/apps/group2/transform.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); @@ -42,7 +42,7 @@ export default function ({ getService }: FtrProviderContext) { }); describe('with data loaded', function () { - const ecIndexPattern = 'ft_ecommerce'; + const ecIndexName = 'ft_ecommerce'; const pivotGroupByEntries = [ { @@ -85,7 +85,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await transform.testResources.createDataViewIfNeeded(ecIndexName, 'order_date'); await transform.testResources.setKibanaTimeZoneToUTC(); }); @@ -93,9 +93,9 @@ export default function ({ getService }: FtrProviderContext) { await transform.api.cleanTransformIndices(); await transform.api.deleteIndices(pivotTransformDestinationIndex); await transform.api.deleteIndices(latestTransformDestinationIndex); - await transform.testResources.deleteIndexPatternByTitle(pivotTransformDestinationIndex); - await transform.testResources.deleteIndexPatternByTitle(latestTransformDestinationIndex); - await transform.testResources.deleteIndexPatternByTitle(ecIndexPattern); + await transform.testResources.deleteDataViewByTitle(pivotTransformDestinationIndex); + await transform.testResources.deleteDataViewByTitle(latestTransformDestinationIndex); + await transform.testResources.deleteDataViewByTitle(ecIndexName); await esArchiver.unload('x-pack/test/functional/es_archives/ml/ecommerce'); await transform.testResources.resetKibanaTimeZone(); }); @@ -110,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { await transform.testExecution.logTestStep( 'transform creation selects the source data and loads the Transform wizard page' ); - await transform.sourceSelection.selectSource(ecIndexPattern); + await transform.sourceSelection.selectSource(ecIndexName); await transform.testExecution.logTestStep( `sets the date picker to the default '15 minutes ago'` @@ -164,8 +164,16 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionInputExists(); await transform.wizard.setTransformDescription(pivotTransformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); + await transform.wizard.assertDestinationIndexValue(pivotTransformId); await transform.wizard.setDestinationIndex(pivotTransformDestinationIndex); await a11y.testAppSnapshot(); @@ -201,7 +209,7 @@ export default function ({ getService }: FtrProviderContext) { await transform.testExecution.logTestStep( 'selects the source data and loads the Transform wizard page' ); - await transform.sourceSelection.selectSource(ecIndexPattern); + await transform.sourceSelection.selectSource(ecIndexName); await transform.testExecution.logTestStep( `sets the date picker to the default '15 minutes ago'` @@ -250,8 +258,16 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionInputExists(); await transform.wizard.setTransformDescription(latestTransformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); + await transform.wizard.assertDestinationIndexValue(latestTransformId); await transform.wizard.setDestinationIndex(latestTransformDestinationIndex); await a11y.testAppSnapshot(); diff --git a/x-pack/test/accessibility/apps/canvas.ts b/x-pack/test/accessibility/apps/group3/canvas.ts similarity index 96% rename from x-pack/test/accessibility/apps/canvas.ts rename to x-pack/test/accessibility/apps/group3/canvas.ts index f3dfa9305fb95..fb6cc672e2ffa 100644 --- a/x-pack/test/accessibility/apps/canvas.ts +++ b/x-pack/test/accessibility/apps/group3/canvas.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/group3/config.ts b/x-pack/test/accessibility/apps/group3/config.ts new file mode 100644 index 0000000000000..94f6c862b5396 --- /dev/null +++ b/x-pack/test/accessibility/apps/group3/config.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services } from '../../services'; +import { pageObjects } from '../../page_objects'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../functional/config.base.js') + ); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('.')], + + pageObjects, + services, + + junit: { + reportName: 'X-Pack Accessibility Tests - Group 3', + }, + }; +} diff --git a/x-pack/test/accessibility/apps/cross_cluster_replication.ts b/x-pack/test/accessibility/apps/group3/cross_cluster_replication.ts similarity index 95% rename from x-pack/test/accessibility/apps/cross_cluster_replication.ts rename to x-pack/test/accessibility/apps/group3/cross_cluster_replication.ts index bc81770de9f4b..db5d70ac26d04 100644 --- a/x-pack/test/accessibility/apps/cross_cluster_replication.ts +++ b/x-pack/test/accessibility/apps/group3/cross_cluster_replication.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ @@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const retry = getService('retry'); - describe('cross cluster replication - a11y tests', async () => { + // github.com/elastic/kibana/issues/153599 + describe.skip('cross cluster replication - a11y tests', async () => { before(async () => { await PageObjects.common.navigateToApp('crossClusterReplication'); }); diff --git a/x-pack/test/accessibility/apps/enterprise_search.ts b/x-pack/test/accessibility/apps/group3/enterprise_search.ts similarity index 98% rename from x-pack/test/accessibility/apps/enterprise_search.ts rename to x-pack/test/accessibility/apps/group3/enterprise_search.ts index b610130d99e0e..a7b7724e43181 100644 --- a/x-pack/test/accessibility/apps/enterprise_search.ts +++ b/x-pack/test/accessibility/apps/group3/enterprise_search.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/graph.ts b/x-pack/test/accessibility/apps/group3/graph.ts similarity index 98% rename from x-pack/test/accessibility/apps/graph.ts rename to x-pack/test/accessibility/apps/group3/graph.ts index 03ca3b2afbfe4..839ea50ce2779 100644 --- a/x-pack/test/accessibility/apps/graph.ts +++ b/x-pack/test/accessibility/apps/group3/graph.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/group3/grok_debugger.ts b/x-pack/test/accessibility/apps/group3/grok_debugger.ts new file mode 100644 index 0000000000000..da630c6bed7b3 --- /dev/null +++ b/x-pack/test/accessibility/apps/group3/grok_debugger.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'security']); + const a11y = getService('a11y'); + const grokDebugger = getService('grokDebugger'); + + // Fixes:https://github.com/elastic/kibana/issues/62102 + describe('Dev tools grok debugger', () => { + before(async () => { + await PageObjects.common.navigateToApp('grokDebugger'); + await grokDebugger.assertExists(); + }); + + it('Dev tools grok debugger set input', async () => { + await grokDebugger.setEventInput('SegerCommaBob'); + await a11y.testAppSnapshot(); + }); + + it('Dev tools grok debugger set pattern', async () => { + await grokDebugger.setPatternInput('%{USERNAME:u}'); + await a11y.testAppSnapshot(); + }); + + it('Dev tools grok debugger simulate', async () => { + await grokDebugger.clickSimulate(); + await a11y.testAppSnapshot(); + }); + }); +} diff --git a/x-pack/test/accessibility/apps/group3/index.ts b/x-pack/test/accessibility/apps/group3/index.ts new file mode 100644 index 0000000000000..d295c2a17a4f0 --- /dev/null +++ b/x-pack/test/accessibility/apps/group3/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('X-Pack Accessibility Tests - Group 3', function () { + loadTestFile(require.resolve('./upgrade_assistant')); + loadTestFile(require.resolve('./canvas')); + loadTestFile(require.resolve('./maps')); + loadTestFile(require.resolve('./graph')); + loadTestFile(require.resolve('./security_solution')); + loadTestFile(require.resolve('./ml_embeddables_in_dashboard')); + loadTestFile(require.resolve('./rules_connectors')); + // Please make sure that the remote clusters, snapshot and restore and + // CCR tests stay in that order. Their execution fails if rearranged. + loadTestFile(require.resolve('./remote_clusters')); + loadTestFile(require.resolve('./snapshot_and_restore')); + loadTestFile(require.resolve('./cross_cluster_replication')); + loadTestFile(require.resolve('./reporting')); + loadTestFile(require.resolve('./enterprise_search')); + + // loadTestFile(require.resolve('./license_management')); + // loadTestFile(require.resolve('./tags')); + // loadTestFile(require.resolve('./search_sessions')); + // loadTestFile(require.resolve('./stack_monitoring')); + // loadTestFile(require.resolve('./watcher')); + // loadTestFile(require.resolve('./rollup_jobs')); + // loadTestFile(require.resolve('./observability')); + }); +}; diff --git a/x-pack/test/accessibility/apps/license_management.ts b/x-pack/test/accessibility/apps/group3/license_management.ts similarity index 95% rename from x-pack/test/accessibility/apps/license_management.ts rename to x-pack/test/accessibility/apps/group3/license_management.ts index 7693ebb197ff1..a71ac90f54ce8 100644 --- a/x-pack/test/accessibility/apps/license_management.ts +++ b/x-pack/test/accessibility/apps/group3/license_management.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['licenseManagement', 'common']); diff --git a/x-pack/test/accessibility/apps/maps.ts b/x-pack/test/accessibility/apps/group3/maps.ts similarity index 98% rename from x-pack/test/accessibility/apps/maps.ts rename to x-pack/test/accessibility/apps/group3/maps.ts index af74466fb8f24..2f69696b5824f 100644 --- a/x-pack/test/accessibility/apps/maps.ts +++ b/x-pack/test/accessibility/apps/group3/maps.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts similarity index 96% rename from x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts rename to x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts index 8f8bc67304c0d..c3278b39096c7 100644 --- a/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts +++ b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts @@ -6,7 +6,7 @@ */ import { Datafeed, Job } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; // @ts-expect-error not full interface const JOB_CONFIG: Job = { @@ -66,7 +66,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.securityCommon.createMlUsers(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); diff --git a/x-pack/test/accessibility/apps/observability.ts b/x-pack/test/accessibility/apps/group3/observability.ts similarity index 95% rename from x-pack/test/accessibility/apps/observability.ts rename to x-pack/test/accessibility/apps/group3/observability.ts index ead89c913d1e1..1d24c1c17be24 100644 --- a/x-pack/test/accessibility/apps/observability.ts +++ b/x-pack/test/accessibility/apps/group3/observability.ts @@ -6,7 +6,7 @@ */ // a11y tests for spaces, space selection and space creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'infraHome']); diff --git a/x-pack/test/accessibility/apps/remote_clusters.ts b/x-pack/test/accessibility/apps/group3/remote_clusters.ts similarity index 99% rename from x-pack/test/accessibility/apps/remote_clusters.ts rename to x-pack/test/accessibility/apps/group3/remote_clusters.ts index deb0e4a090b8c..4b509c88f0525 100644 --- a/x-pack/test/accessibility/apps/remote_clusters.ts +++ b/x-pack/test/accessibility/apps/group3/remote_clusters.ts @@ -6,7 +6,7 @@ */ import { ClusterPayloadEs } from '@kbn/remote-clusters-plugin/common/lib'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; const emptyPrompt = 'remoteClusterListEmptyPrompt'; const createButton = 'remoteClusterEmptyPromptCreateButton'; diff --git a/x-pack/test/accessibility/apps/reporting.ts b/x-pack/test/accessibility/apps/group3/reporting.ts similarity index 97% rename from x-pack/test/accessibility/apps/reporting.ts rename to x-pack/test/accessibility/apps/group3/reporting.ts index f1ac0770c9587..45959e42b383a 100644 --- a/x-pack/test/accessibility/apps/reporting.ts +++ b/x-pack/test/accessibility/apps/group3/reporting.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const { common } = getPageObjects(['common']); diff --git a/x-pack/test/accessibility/apps/rollup_jobs.ts b/x-pack/test/accessibility/apps/group3/rollup_jobs.ts similarity index 98% rename from x-pack/test/accessibility/apps/rollup_jobs.ts rename to x-pack/test/accessibility/apps/group3/rollup_jobs.ts index 2acf48d5f049f..5581a11955e18 100644 --- a/x-pack/test/accessibility/apps/rollup_jobs.ts +++ b/x-pack/test/accessibility/apps/group3/rollup_jobs.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'settings', 'header', 'rollup']); diff --git a/x-pack/test/accessibility/apps/rules_connectors.ts b/x-pack/test/accessibility/apps/group3/rules_connectors.ts similarity index 98% rename from x-pack/test/accessibility/apps/rules_connectors.ts rename to x-pack/test/accessibility/apps/group3/rules_connectors.ts index bc17793717282..bf46735a84fde 100644 --- a/x-pack/test/accessibility/apps/rules_connectors.ts +++ b/x-pack/test/accessibility/apps/group3/rules_connectors.ts @@ -7,7 +7,7 @@ // a11y tests for rules, logs and connectors page -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['settings', 'common']); diff --git a/x-pack/test/accessibility/apps/search_sessions.ts b/x-pack/test/accessibility/apps/group3/search_sessions.ts similarity index 98% rename from x-pack/test/accessibility/apps/search_sessions.ts rename to x-pack/test/accessibility/apps/group3/search_sessions.ts index 5a4ca433e3d21..c400d17263221 100644 --- a/x-pack/test/accessibility/apps/search_sessions.ts +++ b/x-pack/test/accessibility/apps/group3/search_sessions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['searchSessionsManagement']); diff --git a/x-pack/test/accessibility/apps/security_solution.ts b/x-pack/test/accessibility/apps/group3/security_solution.ts similarity index 98% rename from x-pack/test/accessibility/apps/security_solution.ts rename to x-pack/test/accessibility/apps/group3/security_solution.ts index ba7d22fd2d39d..7257f88be8a74 100644 --- a/x-pack/test/accessibility/apps/security_solution.ts +++ b/x-pack/test/accessibility/apps/group3/security_solution.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); diff --git a/x-pack/test/accessibility/apps/snapshot_and_restore.ts b/x-pack/test/accessibility/apps/group3/snapshot_and_restore.ts similarity index 98% rename from x-pack/test/accessibility/apps/snapshot_and_restore.ts rename to x-pack/test/accessibility/apps/group3/snapshot_and_restore.ts index c5f0f52c9c9fe..4c3e5121f9830 100644 --- a/x-pack/test/accessibility/apps/snapshot_and_restore.ts +++ b/x-pack/test/accessibility/apps/group3/snapshot_and_restore.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'settings', 'header', 'snapshotRestore']); diff --git a/x-pack/test/accessibility/apps/stack_monitoring.ts b/x-pack/test/accessibility/apps/group3/stack_monitoring.ts similarity index 97% rename from x-pack/test/accessibility/apps/stack_monitoring.ts rename to x-pack/test/accessibility/apps/group3/stack_monitoring.ts index 87bd4d64d1fb8..64eaa652e702e 100644 --- a/x-pack/test/accessibility/apps/stack_monitoring.ts +++ b/x-pack/test/accessibility/apps/group3/stack_monitoring.ts @@ -7,7 +7,7 @@ // a11y tests for stack monitoring import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'spaceSelector', 'home', 'header', 'security']); diff --git a/x-pack/test/accessibility/apps/tags.ts b/x-pack/test/accessibility/apps/group3/tags.ts similarity index 98% rename from x-pack/test/accessibility/apps/tags.ts rename to x-pack/test/accessibility/apps/group3/tags.ts index 0c0f836cfc894..f885e8ba77c07 100644 --- a/x-pack/test/accessibility/apps/tags.ts +++ b/x-pack/test/accessibility/apps/group3/tags.ts @@ -7,7 +7,7 @@ // a11y tests for spaces, space selection and space creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'settings', 'header', 'home', 'tagManagement']); diff --git a/x-pack/test/accessibility/apps/upgrade_assistant.ts b/x-pack/test/accessibility/apps/group3/upgrade_assistant.ts similarity index 99% rename from x-pack/test/accessibility/apps/upgrade_assistant.ts rename to x-pack/test/accessibility/apps/group3/upgrade_assistant.ts index 47582893e771f..02f823ef6a674 100644 --- a/x-pack/test/accessibility/apps/upgrade_assistant.ts +++ b/x-pack/test/accessibility/apps/group3/upgrade_assistant.ts @@ -11,7 +11,7 @@ */ import type { IndicesCreateRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; const translogSettingsIndexDeprecation: IndicesCreateRequest = { index: 'deprecated_settings', diff --git a/x-pack/test/accessibility/apps/watcher.ts b/x-pack/test/accessibility/apps/group3/watcher.ts similarity index 95% rename from x-pack/test/accessibility/apps/watcher.ts rename to x-pack/test/accessibility/apps/group3/watcher.ts index 85a11db0122ab..72b4e87e50660 100644 --- a/x-pack/test/accessibility/apps/watcher.ts +++ b/x-pack/test/accessibility/apps/group3/watcher.ts @@ -7,7 +7,7 @@ // a11y tests for spaces, space selection and space creation and feature controls -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home', 'header', 'watcher', 'security']); diff --git a/x-pack/test/accessibility/config.ts b/x-pack/test/accessibility/config.ts deleted file mode 100644 index ece39104293a7..0000000000000 --- a/x-pack/test/accessibility/config.ts +++ /dev/null @@ -1,71 +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 { FtrConfigProviderContext } from '@kbn/test'; -import { services } from './services'; -import { pageObjects } from './page_objects'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); - - return { - ...functionalConfig.getAll(), - - testFiles: [ - require.resolve('./apps/login_page'), - require.resolve('./apps/kibana_overview'), - require.resolve('./apps/home'), - require.resolve('./apps/management'), - require.resolve('./apps/grok_debugger'), - require.resolve('./apps/search_profiler'), - require.resolve('./apps/painless_lab'), - // https://github.com/elastic/kibana/issues/153601 - // require.resolve('./apps/uptime'), - require.resolve('./apps/spaces'), - require.resolve('./apps/advanced_settings'), - require.resolve('./apps/dashboard_panel_options'), - require.resolve('./apps/dashboard_controls'), - require.resolve('./apps/users'), - require.resolve('./apps/roles'), - require.resolve('./apps/ingest_node_pipelines'), - require.resolve('./apps/index_lifecycle_management'), - require.resolve('./apps/ml'), - require.resolve('./apps/ml_anomaly_detection'), - require.resolve('./apps/transform'), - require.resolve('./apps/lens'), - require.resolve('./apps/upgrade_assistant'), - require.resolve('./apps/canvas'), - require.resolve('./apps/maps'), - require.resolve('./apps/graph'), - require.resolve('./apps/security_solution'), - require.resolve('./apps/ml_embeddables_in_dashboard'), - require.resolve('./apps/rules_connectors'), - // Please make sure that the remote clusters, snapshot and restore and - // CCR tests stay in that order. Their execution fails if rearranged. - require.resolve('./apps/remote_clusters'), - require.resolve('./apps/snapshot_and_restore'), - // https://github.com/elastic/kibana/issues/153599 - // require.resolve('./apps/cross_cluster_replication'), - require.resolve('./apps/reporting'), - require.resolve('./apps/enterprise_search'), - // require.resolve('./apps/license_management'), - // require.resolve('./apps/tags'), - // require.resolve('./apps/search_sessions'), - // require.resolve('./apps/stack_monitoring'), - // require.resolve('./apps/watcher'), - // require.resolve('./apps/rollup_jobs'), - // require.resolve('./apps/observability'), - ], - - pageObjects, - services, - - junit: { - reportName: 'X-Pack Accessibility Tests', - }, - }; -} diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts index e3ed2144876ad..02983a8607c54 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import moment from 'moment'; import { cleanup, generate } from '@kbn/infra-forge'; import { @@ -92,7 +91,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.5], timeSize: 5, @@ -189,7 +187,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>', threshold: [0.5], timeSize: 5, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts index f02eb9568f68b..20320dc99daee 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import moment from 'moment'; import { Aggregators, @@ -85,7 +84,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.5], timeSize: 5, @@ -181,7 +179,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>', threshold: [0.5], timeSize: 5, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts index 9929491ea4e0e..3312c9e600809 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import moment from 'moment'; import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { format } from 'url'; @@ -98,7 +97,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, + aggType: 'custom', comparator: Comparator.GT, threshold: [7500000], timeSize: 5, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index 71f0a1bed860b..e2b0e84a5cb08 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -11,7 +11,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import moment from 'moment'; import { cleanup, generate } from '@kbn/infra-forge'; import { @@ -97,7 +96,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.9], timeSize: 1, @@ -195,7 +193,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.9], timeSize: 1, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts index c2ca213bae98a..a1057e4ed6f74 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import moment from 'moment'; import { cleanup, generate } from '@kbn/infra-forge'; import { @@ -92,9 +91,8 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, - comparator: Comparator.GT, - threshold: [2], + comparator: Comparator.OUTSIDE_RANGE, + threshold: [1, 2], timeSize: 1, timeUnit: 'm', metrics: [{ name: 'A', filter: '', aggType: Aggregators.COUNT }], @@ -188,9 +186,8 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', - comparator: '>', - threshold: [2], + comparator: Comparator.OUTSIDE_RANGE, + threshold: [1, 2], timeSize: 1, timeUnit: 'm', metrics: [{ name: 'A', filter: '', aggType: 'count' }], @@ -214,7 +211,7 @@ export default function ({ getService }: FtrProviderContext) { `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( - `Document count is 3, above the threshold of 2. (duration: 1 min, data view: ${DATE_VIEW_NAME})` + `Document count is 3, not between the threshold of 1 and 2. (duration: 1 min, data view: ${DATE_VIEW_NAME})` ); expect(resp.hits.hits[0]._source?.value).eql('3'); }); diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts index 74144e8e7c721..c92971eda4058 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts @@ -7,7 +7,6 @@ import moment from 'moment'; import { cleanup, generate } from '@kbn/infra-forge'; -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -91,7 +90,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT_OR_EQ, threshold: [0.2], timeSize: 1, @@ -214,7 +212,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>=', threshold: [0.2], timeSize: 1, diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts index 0cbb1044f887e..6071a67023433 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -74,7 +73,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [7500000], timeSize: 5, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts index 375eeaaffc44a..b7cf41d07e823 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts @@ -102,7 +102,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, - use_alert_data_for_template: false, }, ], enabled: true, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts index 70cdc0f96dfdd..60eb8b6634a35 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/bedrock.ts @@ -13,6 +13,8 @@ import { } from '@kbn/actions-simulators-plugin/server/bedrock_simulation'; import { DEFAULT_TOKEN_LIMIT } from '@kbn/stack-connectors-plugin/common/bedrock/constants'; import { PassThrough } from 'stream'; +import { EventStreamCodec } from '@smithy/eventstream-codec'; +import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../../../../../common/lib'; @@ -411,8 +413,6 @@ export default function bedrockTest({ getService }: FtrProviderContext) { it('should invoke stream with assistant AI body argument formatted to bedrock expectations', async () => { await new Promise((resolve, reject) => { - let responseBody: string = ''; - const passThrough = new PassThrough(); supertest @@ -434,13 +434,14 @@ export default function bedrockTest({ getService }: FtrProviderContext) { assistantLangChain: false, }) .pipe(passThrough); - + const responseBuffer: Uint8Array[] = []; passThrough.on('data', (chunk) => { - responseBody += chunk.toString(); + responseBuffer.push(chunk); }); passThrough.on('end', () => { - expect(responseBody).to.eql('Hello world, what a unique string!'); + const parsed = parseBedrockBuffer(responseBuffer); + expect(parsed).to.eql('Hello world, what a unique string!'); resolve(); }); }); @@ -517,3 +518,46 @@ export default function bedrockTest({ getService }: FtrProviderContext) { }); }); } + +const parseBedrockBuffer = (chunks: Uint8Array[]): string => { + let bedrockBuffer: Uint8Array = new Uint8Array(0); + + return chunks + .map((chunk) => { + bedrockBuffer = concatChunks(bedrockBuffer, chunk); + let messageLength = getMessageLength(bedrockBuffer); + const buildChunks = []; + while (bedrockBuffer.byteLength > 0 && bedrockBuffer.byteLength >= messageLength) { + const extractedChunk = bedrockBuffer.slice(0, messageLength); + buildChunks.push(extractedChunk); + bedrockBuffer = bedrockBuffer.slice(messageLength); + messageLength = getMessageLength(bedrockBuffer); + } + + const awsDecoder = new EventStreamCodec(toUtf8, fromUtf8); + + return buildChunks + .map((bChunk) => { + const event = awsDecoder.decode(bChunk); + const body = JSON.parse( + Buffer.from(JSON.parse(new TextDecoder().decode(event.body)).bytes, 'base64').toString() + ); + return body.completion; + }) + .join(''); + }) + .join(''); +}; + +function concatChunks(a: Uint8Array, b: Uint8Array): Uint8Array { + const newBuffer = new Uint8Array(a.length + b.length); + newBuffer.set(a); + newBuffer.set(b, a.length); + return newBuffer; +} + +function getMessageLength(buffer: Uint8Array): number { + if (buffer.byteLength === 0) return 0; + const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + return view.getUint32(0, false); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts index ffd11e3fabd38..c948791e5ea49 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts @@ -116,7 +116,6 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { params: {}, connector_type_id: 'test.noop', uuid: response.body.rules[0].actions[0].uuid, - use_alert_data_for_template: false, }, ]); expect(response.statusCode).to.eql(200); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/create_maintenance_window.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/create_maintenance_window.ts index cf0e2a3dcf6a7..05a16cf992848 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/create_maintenance_window.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/create_maintenance_window.ts @@ -25,6 +25,32 @@ export default function createMaintenanceWindowTests({ getService }: FtrProvider tzid: 'UTC', freq: 2, // weekly }, + scoped_query: { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }, }; afterEach(() => objectRemover.removeAll()); @@ -69,6 +95,7 @@ export default function createMaintenanceWindowTests({ getService }: FtrProvider expect(response.body.r_rule.dtstart).to.eql(createParams.r_rule.dtstart); expect(response.body.events.length).to.be.greaterThan(0); expect(response.body.status).to.eql('running'); + expect(response.body.scoped_query.kql).to.eql("_id: '1234'"); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -102,5 +129,19 @@ export default function createMaintenanceWindowTests({ getService }: FtrProvider }) .expect(400); }); + + it('should throw if creating maintenance window with invalid scoped query', async () => { + await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + ...createParams, + scoped_query: { + kql: 'invalid_kql:', + filters: [], + }, + }) + .expect(400); + }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts index c5d38a1c2a056..8d26f316cb840 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/maintenance_window/update_maintenance_window.ts @@ -16,6 +16,33 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); + const scopedQuery = { + kql: "_id: '1234'", + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + key: 'kibana.alert.action_group', + field: 'kibana.alert.action_group', + params: { + query: 'test', + }, + type: 'phrase', + }, + $state: { + store: 'appState', + }, + query: { + match_phrase: { + 'kibana.alert.action_group': 'test', + }, + }, + }, + ], + }; + describe('updateMaintenanceWindow', () => { const objectRemover = new ObjectRemover(supertest); const createParams = { @@ -26,6 +53,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider tzid: 'UTC', freq: 2, // weekly }, + scoped_query: scopedQuery, }; afterEach(() => objectRemover.removeAll()); @@ -82,6 +110,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider expect(response.body.r_rule.dtstart).to.eql(createParams.r_rule.dtstart); expect(response.body.events.length).to.be.greaterThan(0); expect(response.body.status).to.eql('running'); + expect(response.body.scoped_query.kql).to.eql("_id: '1234'"); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -156,6 +185,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider until: moment.utc().add(1, 'week').toISOString(), }, category_ids: ['management'], + scoped_query: scopedQuery, }) .expect(200); @@ -183,6 +213,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider ...createParams, r_rule: updatedRRule, category_ids: null, + scoped_query: null, }) .expect(200); @@ -194,6 +225,7 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider expect(response.body.data[0].id).to.eql(createdMaintenanceWindow.id); expect(response.body.data[0].r_rule).to.eql(updatedRRule); expect(response.body.data[0].category_ids).to.eql(null); + expect(response.body.data[0].scoped_query).to.eql(null); }); it('should throw if updating maintenance window with invalid category ids', async () => { @@ -230,5 +262,46 @@ export default function updateMaintenanceWindowTests({ getService }: FtrProvider .send({ category_ids: ['something-else'] }) .expect(400); }); + + it('should throw if updating maintenance window with invalid scoped query', async () => { + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 2, // weekly + count: 1, + }, + scoped_query: scopedQuery, + }) + .expect(200); + + objectRemover.add( + 'space1', + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + await supertest + .post( + `${getUrlPrefix('space1')}/internal/alerting/rules/maintenance_window/${ + createdMaintenanceWindow.id + }` + ) + .set('kbn-xsrf', 'foo') + .send({ + scoped_query: { + kql: 'invalid_kql:', + filters: [], + }, + }) + .expect(400); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts index 0577425103a8c..eb9f90cb41f2a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts @@ -78,7 +78,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, - use_alert_data_for_template: false, }, ], enabled: true, @@ -182,7 +181,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, - use_alert_data_for_template: false, }, { id: 'my-slack1', @@ -192,7 +190,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { message: 'something important happened!', }, uuid: response.body.actions[1].uuid, - use_alert_data_for_template: false, }, { id: 'system-connector-test.system-action', @@ -200,7 +197,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { connector_type_id: 'test.system-action', params: {}, uuid: response.body.actions[2].uuid, - use_alert_data_for_template: false, }, ], enabled: true, diff --git a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts index 3168dfdfc5ecf..352282b4ba41f 100644 --- a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts +++ b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts @@ -86,9 +86,14 @@ export default ({ getService }: FtrProviderContext) => { const groupActions = getGroupActions(data, apiVersion); const groups = groupActions.flatMap((d) => d.payload); - expect(orderBy(groups, ['docCount'], ['desc'])).to.eql( - orderBy(testData.expected.groups, ['docCount'], ['desc']), - 'Grouping result does not match expected values.' + const actualGroups = orderBy(groups, ['docCount'], ['desc']); + const expectedGroups = orderBy(testData.expected.groups, ['docCount'], ['desc']); + + expect(actualGroups).to.eql( + expectedGroups, + `Grouping result does not match expected values. Expected ${JSON.stringify( + expectedGroups + )}, got ${JSON.stringify(actualGroups)}` ); const groupHistogramActions = getGroupHistogramActions(data, apiVersion); diff --git a/x-pack/test/api_integration/apis/aiops/test_data.ts b/x-pack/test/api_integration/apis/aiops/test_data.ts index 291779ed6c7b2..3c1793ab9efe3 100644 --- a/x-pack/test/api_integration/apis/aiops/test_data.ts +++ b/x-pack/test/api_integration/apis/aiops/test_data.ts @@ -12,6 +12,8 @@ import { significantTerms as artificialLogSignificantTerms } from '@kbn/aiops-pl import { significantLogPatterns as artificialLogSignificantLogPatterns } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/significant_log_patterns'; import { finalSignificantItemGroups as artificialLogsSignificantItemGroups } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/final_significant_item_groups'; import { finalSignificantItemGroupsTextfield as artificialLogsSignificantItemGroupsTextfield } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/final_significant_item_groups_textfield'; +import { topTerms } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/top_terms'; +import { topTermsGroups } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/top_terms_groups'; import type { AiopsLogRateAnalysisSchema, @@ -39,9 +41,9 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 35, + chunksLength: 36, chunksLengthGroupOnly: 5, - actionsLength: 34, + actionsLength: 35, actionsLengthGroupOnly: 4, noIndexChunksLength: 4, noIndexActionsLength: 3, @@ -78,14 +80,14 @@ export const getLogRateAnalysisTestData = (): Array(): Array, expected: { - chunksLength: 27, + chunksLength: 28, chunksLengthGroupOnly: 11, - actionsLength: 26, + actionsLength: 27, actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, @@ -104,6 +106,60 @@ export const getLogRateAnalysisTestData = (): Array, + expected: { + chunksLength: 62, + chunksLengthGroupOnly: 32, + actionsLength: 61, + actionsLengthGroupOnly: 31, + noIndexChunksLength: 4, + noIndexActionsLength: 3, + significantItems: topTerms, + groups: topTermsGroups, + histogramLength: 20, + }, + }, + { + testName: 'artificial_logs_with_dip_zerodocsfallback', + dataGenerator: 'artificial_logs_with_dip_zerodocsfallback', + requestBody: { + start: 0, + end: 1768855600010, + searchQuery: '{"match_all":{}}', + timeFieldName: '@timestamp', + index: 'artificial_logs_with_dip_zerodocsfallback', + baselineMin: 1768855600000, + baselineMax: 1768855600010, + deviationMin: 1668855600000, + deviationMax: 1668924000000, + grouping: true, + } as AiopsLogRateAnalysisSchema, + expected: { + chunksLength: 62, + chunksLengthGroupOnly: 32, + actionsLength: 61, + actionsLengthGroupOnly: 31, + noIndexChunksLength: 4, + noIndexActionsLength: 3, + significantItems: topTerms, + groups: topTermsGroups, + histogramLength: 20, + }, + }, { testName: 'artificial_logs_with_spike_textfield', dataGenerator: 'artificial_logs_with_spike_textfield', @@ -120,9 +176,9 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 30, + chunksLength: 31, chunksLengthGroupOnly: 11, - actionsLength: 29, + actionsLength: 30, actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, @@ -132,14 +188,14 @@ export const getLogRateAnalysisTestData = (): Array(): Array, expected: { - chunksLength: 27, + chunksLength: 28, chunksLengthGroupOnly: 11, - actionsLength: 26, + actionsLength: 27, actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, @@ -174,9 +230,9 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 30, + chunksLength: 31, chunksLengthGroupOnly: 11, - actionsLength: 29, + actionsLength: 30, actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/index.ts b/x-pack/test/api_integration/apis/asset_manager/tests/index.ts index e2ea53990b216..12274a3fc6ab0 100644 --- a/x-pack/test/api_integration/apis/asset_manager/tests/index.ts +++ b/x-pack/test/api_integration/apis/asset_manager/tests/index.ts @@ -12,6 +12,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./containers')); loadTestFile(require.resolve('./hosts')); loadTestFile(require.resolve('./services')); + loadTestFile(require.resolve('./pods')); loadTestFile(require.resolve('./sample_assets')); }); } diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/pods.ts b/x-pack/test/api_integration/apis/asset_manager/tests/pods.ts new file mode 100644 index 0000000000000..eb88049b6511f --- /dev/null +++ b/x-pack/test/api_integration/apis/asset_manager/tests/pods.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { timerange, infra } from '@kbn/apm-synthtrace-client'; +import expect from '@kbn/expect'; +import { Asset } from '@kbn/assetManager-plugin/common/types_api'; +import * as routePaths from '@kbn/assetManager-plugin/common/constants_routes'; +import { FtrProviderContext } from '../types'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const synthtrace = getService('infraSynthtraceEsClient'); + + describe(`GET ${routePaths.GET_PODS}`, () => { + const from = new Date(Date.now() - 1000 * 60 * 2).toISOString(); + const to = new Date().toISOString(); + + beforeEach(async () => { + await synthtrace.clean(); + }); + + it('should return pod assets', async () => { + await synthtrace.index(generatePodsData({ from, to, count: 5 })); + + const response = await supertest + .get(routePaths.GET_PODS) + .query({ + from, + to, + }) + .expect(200); + + expect(response.body).to.have.property('pods'); + expect(response.body.pods.length).to.equal(5); + }); + + it('should return a specific pod asset by EAN', async () => { + await synthtrace.index(generatePodsData({ from, to, count: 5 })); + const testEan = 'pod:pod-uid-1'; + + const response = await supertest + .get(routePaths.GET_PODS) + .query({ + from, + to, + stringFilters: JSON.stringify({ ean: testEan }), + }) + .expect(200); + + expect(response.body).to.have.property('pods'); + expect(response.body.pods.length).to.equal(1); + expect(response.body.pods[0]['asset.ean']).to.equal(testEan); + }); + + it('should return a filtered list of pods assets by ID wildcard pattern', async () => { + await synthtrace.index(generatePodsData({ from, to, count: 15 })); + const testIdPattern = '*id-1*'; + + const response = await supertest + .get(routePaths.GET_PODS) + .query({ + from, + to, + stringFilters: JSON.stringify({ id: testIdPattern }), + }) + .expect(200); + + expect(response.body).to.have.property('pods'); + expect(response.body.pods.length).to.equal(6); + + const ids = response.body.pods.map((result: Asset) => result['asset.id'][0]); + + expect(ids).to.eql([ + 'pod-uid-1', + 'pod-uid-10', + 'pod-uid-11', + 'pod-uid-12', + 'pod-uid-13', + 'pod-uid-14', + ]); + }); + }); +} + +function generatePodsData({ from, to, count = 1 }: { from: string; to: string; count: number }) { + const range = timerange(from, to); + + const pods = Array(count) + .fill(0) + .map((_, idx) => infra.pod(`pod-uid-${idx}`, `node-name-${idx}`)); + + return range + .interval('1m') + .rate(1) + .generator((timestamp) => pods.map((pod) => pod.metrics().timestamp(timestamp))); +} diff --git a/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.helpers.ts deleted file mode 100644 index 8dabd89bf7c23..0000000000000 --- a/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.helpers.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { API_BASE_PATH } from './constants'; - -export const registerHelpers = ({ supertest }: { supertest: any }) => { - const getNodesPlugins = () => supertest.get(`${API_BASE_PATH}/nodes/plugins`); - - return { - getNodesPlugins, - }; -}; diff --git a/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.ts b/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.ts index e885b677aaffb..72ca2d8fee5ba 100644 --- a/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.ts +++ b/x-pack/test/api_integration/apis/management/index_management/cluster_nodes.ts @@ -8,12 +8,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { registerHelpers } from './cluster_nodes.helpers'; +import { clusterNodesApi } from './lib/cluster_nodes.api'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - const { getNodesPlugins } = registerHelpers({ supertest }); + const { getNodesPlugins } = clusterNodesApi(getService); describe('nodes', () => { it('should fetch the nodes plugins', async () => { diff --git a/x-pack/test/api_integration/apis/management/index_management/component_templates.ts b/x-pack/test/api_integration/apis/management/index_management/component_templates.ts index 01e7ae46ce8fe..f41dedccb126f 100644 --- a/x-pack/test/api_integration/apis/management/index_management/component_templates.ts +++ b/x-pack/test/api_integration/apis/management/index_management/component_templates.ts @@ -8,26 +8,35 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -// @ts-ignore -import { initElasticsearchHelpers } from './lib'; -// @ts-ignore +import { componentTemplatesApi } from './lib/component_templates.api'; +import { componentTemplateHelpers } from './lib/component_template.helpers'; import { API_BASE_PATH } from './constants'; +const CACHE_TEMPLATES = true; + export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); const supertest = getService('supertest'); const { createComponentTemplate, - createIndexTemplate, - cleanUpIndexTemplates, - cleanUpComponentTemplates, + getAllComponentTemplates, + getOneComponentTemplate, + updateComponentTemplate, deleteComponentTemplate, + getComponentTemplateDatastreams, + } = componentTemplatesApi(getService); + const { + addDatastream, + addIndexTemplate, + addComponentTemplate, + removeComponentTemplate, cleanupDatastreams, - createDatastream, - } = initElasticsearchHelpers(getService); + cleanUpIndexTemplates, + cleanUpComponentTemplates, + } = componentTemplateHelpers(getService); - // FLAKY: https://github.com/elastic/kibana/issues/170999 - describe.skip('Component templates', function () { + describe('Component templates', function () { after(async () => { await cleanUpIndexTemplates(); await cleanUpComponentTemplates(); @@ -63,20 +72,16 @@ export default function ({ getService }: FtrProviderContext) { // Create component template to verify GET requests before(async () => { try { - await createComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, true); + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); } catch (err) { - // eslint-disable-next-line no-console - console.log('[Setup error] Error creating component template'); + log.debug('[Setup error] Error creating component template'); throw err; } }); describe('all component templates', () => { it('should return an array of component templates', async () => { - const { body: componentTemplates } = await supertest - .get(`${API_BASE_PATH}/component_templates`) - .set('kbn-xsrf', 'xxx') - .expect(200); + const { body: componentTemplates } = await getAllComponentTemplates().expect(200); const testComponentTemplate = componentTemplates.find( ({ name }: { name: string }) => name === COMPONENT_NAME @@ -95,9 +100,7 @@ export default function ({ getService }: FtrProviderContext) { describe('one component template', () => { it('should return a single component template', async () => { - const uri = `${API_BASE_PATH}/component_templates/${COMPONENT_NAME}`; - - const { body } = await supertest.get(uri).set('kbn-xsrf', 'xxx').expect(200); + const { body } = await getOneComponentTemplate(COMPONENT_NAME).expect(200); expect(body).to.eql({ name: COMPONENT_NAME, @@ -118,53 +121,48 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { // Clean up any component templates created in test cases await Promise.all( - [COMPONENT_NAME, REQUIRED_FIELDS_COMPONENT_NAME].map(deleteComponentTemplate) + [COMPONENT_NAME, REQUIRED_FIELDS_COMPONENT_NAME].map(removeComponentTemplate) ).catch((err) => { - // eslint-disable-next-line no-console - console.log(`[Cleanup error] Error deleting component templates: ${err.message}`); + log.debug(`[Cleanup error] Error deleting component templates: ${err.message}`); throw err; }); }); it('should create a component template', async () => { - const { body } = await supertest - .post(`${API_BASE_PATH}/component_templates`) - .set('kbn-xsrf', 'xxx') - .send({ - name: COMPONENT_NAME, - version: 1, - template: { - settings: { - number_of_shards: 1, - }, - aliases: { - alias1: {}, - }, - mappings: { - properties: { - host_name: { - type: 'keyword', - }, + const { body } = await createComponentTemplate(COMPONENT_NAME, { + version: 1, + template: { + settings: { + number_of_shards: 1, + }, + aliases: { + alias1: {}, + }, + mappings: { + properties: { + host_name: { + type: 'keyword', }, }, - lifecycle: { - enabled: true, - data_retention: '2d', - }, }, - _meta: { - description: 'set number of shards to one', - serialization: { - class: 'MyComponentTemplate', - id: 10, - }, + lifecycle: { + // @ts-expect-error @elastic/elasticsearch enabled prop is still not in the ES types + enabled: true, + data_retention: '2d', }, - _kbnMeta: { - usedBy: [], - isManaged: false, + }, + _meta: { + description: 'set number of shards to one', + serialization: { + class: 'MyComponentTemplate', + id: 10, }, - }) - .expect(200); + }, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(200); expect(body).to.eql({ acknowledged: true, @@ -172,19 +170,14 @@ export default function ({ getService }: FtrProviderContext) { }); it('should create a component template with only required fields', async () => { - const { body } = await supertest - .post(`${API_BASE_PATH}/component_templates`) - .set('kbn-xsrf', 'xxx') + const { body } = await createComponentTemplate(REQUIRED_FIELDS_COMPONENT_NAME, { // Excludes version and _meta fields - .send({ - name: REQUIRED_FIELDS_COMPONENT_NAME, - template: {}, - _kbnMeta: { - usedBy: [], - isManaged: false, - }, - }) - .expect(200); + template: {}, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(200); expect(body).to.eql({ acknowledged: true, @@ -192,18 +185,13 @@ export default function ({ getService }: FtrProviderContext) { }); it('should not allow creation of a component template with the same name of an existing one', async () => { - const { body } = await supertest - .post(`${API_BASE_PATH}/component_templates`) - .set('kbn-xsrf', 'xxx') - .send({ - name: COMPONENT_NAME, - template: {}, - _kbnMeta: { - usedBy: [], - isManaged: false, - }, - }) - .expect(409); + const { body } = await createComponentTemplate(COMPONENT_NAME, { + template: {}, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(409); expect(body).to.eql({ statusCode: 409, @@ -242,30 +230,22 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { // Create component template that can be used to test PUT request try { - await createComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, true); + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); } catch (err) { - // eslint-disable-next-line no-console - console.log('[Setup error] Error creating component template'); + log.debug('[Setup error] Error creating component template'); throw err; } }); it('should allow an existing component template to be updated', async () => { - const uri = `${API_BASE_PATH}/component_templates/${COMPONENT_NAME}`; - - const { body } = await supertest - .put(uri) - .set('kbn-xsrf', 'xxx') - .send({ - ...COMPONENT, - name: COMPONENT_NAME, - version: 1, - _kbnMeta: { - usedBy: [], - isManaged: false, - }, - }) - .expect(200); + const { body } = await updateComponentTemplate(COMPONENT_NAME, { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(200); expect(body).to.eql({ acknowledged: true, @@ -273,21 +253,14 @@ export default function ({ getService }: FtrProviderContext) { }); it('should not allow a non-existing component template to be updated', async () => { - const uri = `${API_BASE_PATH}/component_templates/component_does_not_exist`; - - const { body } = await supertest - .put(uri) - .set('kbn-xsrf', 'xxx') - .send({ - ...COMPONENT, - name: 'component_does_not_exist', - version: 1, - _kbnMeta: { - usedBy: [], - isManaged: false, - }, - }) - .expect(404); + const { body } = await updateComponentTemplate('component_does_not_exist', { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(404); expect(body).to.eql({ statusCode: 404, @@ -329,20 +302,17 @@ export default function ({ getService }: FtrProviderContext) { // Create several component templates that can be used to test deletion await Promise.all( [componentTemplateA, componentTemplateB, componentTemplateC, componentTemplateD].map( - (template) => createComponentTemplate(template, false) + (template) => addComponentTemplate(template, !CACHE_TEMPLATES) ) ).catch((err) => { - // eslint-disable-next-line no-console - console.log(`[Setup error] Error creating component templates: ${err.message}`); + log.debug(`[Setup error] Error creating component templates: ${err.message}`); throw err; }); }); it('should delete a component template', async () => { const { name } = componentTemplateA; - const uri = `${API_BASE_PATH}/component_templates/${name}`; - - const { body } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200); + const { body } = await deleteComponentTemplate(name).expect(200); expect(body).to.eql({ itemsDeleted: [name], @@ -354,11 +324,11 @@ export default function ({ getService }: FtrProviderContext) { const { name: componentTemplate1Name } = componentTemplateB; const { name: componentTemplate2Name } = componentTemplateC; - const uri = `${API_BASE_PATH}/component_templates/${componentTemplate1Name},${componentTemplate2Name}`; - const { body: { itemsDeleted, errors }, - } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200); + } = await deleteComponentTemplate( + `${componentTemplate1Name},${componentTemplate2Name}` + ).expect(200); expect(errors).to.eql([]); @@ -372,9 +342,9 @@ export default function ({ getService }: FtrProviderContext) { const COMPONENT_DOES_NOT_EXIST = 'component_does_not_exist'; const { name: componentTemplateName } = componentTemplateD; - const uri = `${API_BASE_PATH}/component_templates/${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}`; - - const { body } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200); + const { body } = await deleteComponentTemplate( + `${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}` + ).expect(200); expect(body.itemsDeleted).to.eql([componentTemplateName]); expect(body.errors[0].name).to.eql(COMPONENT_DOES_NOT_EXIST); @@ -442,20 +412,17 @@ export default function ({ getService }: FtrProviderContext) { // Create component template to verify GET requests before(async () => { try { - await createComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, true); - await createIndexTemplate({ body: TEMPLATE, name: TEMPLATE_NAME }, true); + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); + await addIndexTemplate({ body: TEMPLATE, name: TEMPLATE_NAME }, CACHE_TEMPLATES); } catch (err) { - // eslint-disable-next-line no-console - console.log('[Setup error] Error creating component template'); + log.debug('[Setup error] Error creating component template'); throw err; } }); describe('without datastreams', () => { it('should return no datastreams', async () => { - const uri = `${API_BASE_PATH}/component_templates/${COMPONENT_NAME}/datastreams`; - - const { body } = await supertest.get(uri).set('kbn-xsrf', 'xxx').expect(200); + const { body } = await getComponentTemplateDatastreams(COMPONENT_NAME).expect(200); expect(body).to.eql({ data_streams: [] }); }); @@ -463,12 +430,10 @@ export default function ({ getService }: FtrProviderContext) { describe('with datastreams', () => { before(async () => { - await createDatastream(DATASTREAM_NAME); + await addDatastream(DATASTREAM_NAME, CACHE_TEMPLATES); }); it('should return datastreams', async () => { - const uri = `${API_BASE_PATH}/component_templates/${COMPONENT_NAME}/datastreams`; - - const { body } = await supertest.get(uri).set('kbn-xsrf', 'xxx').expect(200); + const { body } = await getComponentTemplateDatastreams(COMPONENT_NAME).expect(200); expect(body).to.eql({ data_streams: ['logs-test-component-template-default'] }); }); diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index 6ed1ec9ed1c4c..791e23149aff1 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -9,85 +9,21 @@ import expect from '@kbn/expect'; import { DataStream } from '@kbn/index-management-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; -// @ts-ignore import { API_BASE_PATH } from './constants'; +import { datastreamsHelpers } from './lib/datastreams.helpers'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); - - const createDataStream = async (name: string) => { - // A data stream requires an index template before it can be created. - await es.indices.putIndexTemplate({ - name, - body: { - // We need to match the names of backing indices with this template. - index_patterns: [name + '*'], - template: { - mappings: { - properties: { - '@timestamp': { - type: 'date', - }, - }, - }, - lifecycle: { - // @ts-expect-error @elastic/elasticsearch enabled prop is not typed yet - enabled: true, - }, - }, - data_stream: {}, - }, - }); - await es.indices.createDataStream({ name }); - }; - - const updateIndexTemplateMappings = async (name: string, mappings: any) => { - await es.indices.putIndexTemplate({ - name, - body: { - // We need to match the names of backing indices with this template. - index_patterns: [name + '*'], - template: { - mappings, - }, - data_stream: {}, - }, - }); - }; - - const getDatastream = async (name: string) => { - const { - data_streams: [datastream], - } = await es.indices.getDataStream({ name }); - return datastream; - }; - - const getMapping = async (name: string) => { - const res = await es.indices.getMapping({ index: name }); - - return Object.values(res)[0]!.mappings; - }; - - const deleteComposableIndexTemplate = async (name: string) => { - await es.indices.deleteIndexTemplate({ name }); - }; - - const deleteDataStream = async (name: string) => { - await es.indices.deleteDataStream({ name }); - await deleteComposableIndexTemplate(name); - }; - - const assertDataStreamStorageSizeExists = (storageSize: string, storageSizeBytes: number) => { - // Storage size of a document doesn't look like it would be deterministic (could vary depending - // on how ES, Lucene, and the file system interact), so we'll just assert its presence and - // type. - expect(storageSize).to.be.ok(); - expect(typeof storageSize).to.be('string'); - expect(storageSizeBytes).to.be.ok(); - expect(typeof storageSizeBytes).to.be('number'); - }; + const { + createDataStream, + deleteDataStream, + assertDataStreamStorageSizeExists, + deleteComposableIndexTemplate, + updateIndexTemplateMappings, + getMapping, + getDatastream, + } = datastreamsHelpers(getService); describe('Data streams', function () { describe('Get', () => { diff --git a/x-pack/test/api_integration/apis/management/index_management/indices.helpers.js b/x-pack/test/api_integration/apis/management/index_management/indices.helpers.js deleted file mode 100644 index 368272858ea2c..0000000000000 --- a/x-pack/test/api_integration/apis/management/index_management/indices.helpers.js +++ /dev/null @@ -1,53 +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 { API_BASE_PATH } from './constants'; - -export const registerHelpers = ({ supertest }) => { - const executeActionOnIndices = (index, urlParam, args) => { - const indices = Array.isArray(index) ? index : [index]; - - return supertest - .post(`${API_BASE_PATH}/indices/${urlParam}`) - .set('kbn-xsrf', 'xxx') - .send({ indices, ...args }); - }; - - const closeIndex = (index) => executeActionOnIndices(index, 'close'); - - const openIndex = (index) => executeActionOnIndices(index, 'open'); - - const deleteIndex = (index) => executeActionOnIndices(index, 'delete'); - - const flushIndex = (index) => executeActionOnIndices(index, 'flush'); - - const refreshIndex = (index) => executeActionOnIndices(index, 'refresh'); - - const forceMerge = (index, args) => executeActionOnIndices(index, 'forcemerge', args); - - const unfreeze = (index) => executeActionOnIndices(index, 'unfreeze'); - - const clearCache = (index) => executeActionOnIndices(index, 'clear_cache'); - - const list = () => supertest.get(`${API_BASE_PATH}/indices`); - - const reload = (indexNames) => - supertest.post(`${API_BASE_PATH}/indices/reload`).set('kbn-xsrf', 'xxx').send({ indexNames }); - - return { - closeIndex, - openIndex, - deleteIndex, - flushIndex, - refreshIndex, - forceMerge, - unfreeze, - list, - reload, - clearCache, - }; -}; diff --git a/x-pack/test/api_integration/apis/management/index_management/indices.js b/x-pack/test/api_integration/apis/management/index_management/indices.ts similarity index 75% rename from x-pack/test/api_integration/apis/management/index_management/indices.js rename to x-pack/test/api_integration/apis/management/index_management/indices.ts index 8d012f7a02d41..aa2ff5966c4df 100644 --- a/x-pack/test/api_integration/apis/management/index_management/indices.js +++ b/x-pack/test/api_integration/apis/management/index_management/indices.ts @@ -7,19 +7,13 @@ import expect from '@kbn/expect'; -import { initElasticsearchHelpers } from './lib'; -import { registerHelpers } from './indices.helpers'; import { sortedExpectedIndexKeys } from './constants'; +import { indicesApi } from './lib/indices.api'; +import { indicesHelpers } from './lib/indices.helpers'; +import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ getService }) { - const supertest = getService('supertest'); - - const { - createIndex, - catIndex, - indexStats, - cleanUp: cleanUpEsResources, - } = initElasticsearchHelpers(getService); +export default function ({ getService }: FtrProviderContext) { + const { createIndex, deleteAllIndices, catIndex, indexStats } = indicesHelpers(getService); const { closeIndex, @@ -32,10 +26,10 @@ export default function ({ getService }) { list, reload, clearCache, - } = registerHelpers({ supertest }); + } = indicesApi(getService); describe('indices', () => { - after(() => Promise.all([cleanUpEsResources()])); + after(async () => await deleteAllIndices()); describe('clear cache', () => { it('should clear the cache on a single index', async () => { @@ -45,9 +39,6 @@ export default function ({ getService }) { }); describe('close', function () { - // The Cloud backend disallows users from closing indices. - this.tags(['skipCloud']); - it('should close an index', async () => { const index = await createIndex(); @@ -68,10 +59,6 @@ export default function ({ getService }) { }); describe('open', function () { - // The Cloud backend disallows users from closing indices, so there's no point testing - // the open behavior. - this.tags(['skipCloud']); - it('should open an index', async () => { const index = await createIndex(); @@ -98,12 +85,12 @@ export default function ({ getService }) { const index = await createIndex(); const { body: indices1 } = await catIndex(undefined, 'i'); - expect(indices1.map((index) => index.i)).to.contain(index); + expect(indices1.map((indexItem) => indexItem.i)).to.contain(index); - await deleteIndex([index]).expect(200); + await deleteIndex(index).expect(200); const { body: indices2 } = await catIndex(undefined, 'i'); - expect(indices2.map((index) => index.i)).not.to.contain(index); + expect(indices2.map((indexItem) => indexItem.i)).not.to.contain(index); }); it('should require index or indices to be provided', async () => { @@ -119,6 +106,7 @@ export default function ({ getService }) { const { body: { indices: indices1 }, } = await indexStats(index, 'flush'); + // @ts-ignore expect(indices1[index].total.flush.total).to.be(0); await flushIndex(index).expect(200); @@ -126,6 +114,7 @@ export default function ({ getService }) { const { body: { indices: indices2 }, } = await indexStats(index, 'flush'); + // @ts-ignore expect(indices2[index].total.flush.total).to.be(1); }); }); @@ -137,6 +126,7 @@ export default function ({ getService }) { const { body: { indices: indices1 }, } = await indexStats(index, 'refresh'); + // @ts-ignore const previousRefreshes = indices1[index].total.refresh.total; await refreshIndex(index).expect(200); @@ -144,6 +134,7 @@ export default function ({ getService }) { const { body: { indices: indices2 }, } = await indexStats(index, 'refresh'); + // @ts-ignore expect(indices2[index].total.refresh.total).to.be(previousRefreshes + 1); }); }); @@ -175,8 +166,6 @@ export default function ({ getService }) { }); describe('list', function () { - this.tags(['skipCloud']); - it('should list all the indices with the expected properties and data enrichers', async function () { // Create an index that we can assert against await createIndex('test_index'); @@ -185,7 +174,7 @@ export default function ({ getService }) { const { body: indices } = await list().expect(200); // Find the "test_index" created to verify expected keys - const indexCreated = indices.find((index) => index.name === 'test_index'); + const indexCreated = indices.find((index: { name: string }) => index.name === 'test_index'); const sortedReceivedKeys = Object.keys(indexCreated).sort(); @@ -194,19 +183,17 @@ export default function ({ getService }) { }); describe('reload', function () { - describe('(not on Cloud)', function () { - this.tags(['skipCloud']); - - it('should list all the indices with the expected properties and data enrichers', async function () { - // create an index to assert against, otherwise the test is flaky - await createIndex('reload-test-index'); - const { body } = await reload().expect(200); - - const indexCreated = body.find((index) => index.name === 'reload-test-index'); - const sortedReceivedKeys = Object.keys(indexCreated).sort(); - expect(sortedReceivedKeys).to.eql(sortedExpectedIndexKeys); - expect(body.length > 1).to.be(true); // to contrast it with the next test - }); + it('should list all the indices with the expected properties and data enrichers', async function () { + // create an index to assert against, otherwise the test is flaky + await createIndex('reload-test-index'); + const { body } = await reload().expect(200); + + const indexCreated = body.find( + (index: { name: string }) => index.name === 'reload-test-index' + ); + const sortedReceivedKeys = Object.keys(indexCreated).sort(); + expect(sortedReceivedKeys).to.eql(sortedExpectedIndexKeys); + expect(body.length > 1).to.be(true); // to contrast it with the next test }); it('should allow reloading only certain indices', async () => { diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/cluster_nodes.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/cluster_nodes.api.ts new file mode 100644 index 0000000000000..c0bca06cd0e96 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/cluster_nodes.api.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 { API_BASE_PATH } from '../constants'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function clusterNodesApi(getService: FtrProviderContext['getService']) { + const supertest = getService('supertest'); + + const getNodesPlugins = () => + supertest + .get(`${API_BASE_PATH}/nodes/plugins`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + return { + getNodesPlugins, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/component_template.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/lib/component_template.helpers.ts new file mode 100644 index 0000000000000..9d609e8a277de --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/component_template.helpers.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function componentTemplateHelpers(getService: FtrProviderContext['getService']) { + const log = getService('log'); + const es = getService('es'); + + let componentTemplatesCreated: string[] = []; + let indexTemplatesCreated: string[] = []; + let datastreamCreated: string[] = []; + + const removeComponentTemplate = (name: string) => es.cluster.deleteComponentTemplate({ name }); + + const removeDatastream = (datastream: string) => + es.indices.deleteDataStream({ name: datastream }); + + const removeIndexTemplate = (name: string) => + es.indices.deleteIndexTemplate({ name }, { meta: true }); + + const addComponentTemplate = (componentTemplate: any, shouldCacheTemplate: boolean) => { + if (shouldCacheTemplate) { + componentTemplatesCreated.push(componentTemplate.name); + } + + return es.cluster.putComponentTemplate(componentTemplate, { meta: true }); + }; + + const addIndexTemplate = (indexTemplate: any, shouldCacheTemplate: boolean) => { + if (shouldCacheTemplate) { + indexTemplatesCreated.push(indexTemplate.name); + } + + return es.indices.putIndexTemplate(indexTemplate, { meta: true }); + }; + + const addDatastream = (datastream: string, shouldCacheTemplate: boolean) => { + if (shouldCacheTemplate) { + datastreamCreated.push(datastream); + } + + return es.indices.createDataStream({ name: datastream }); + }; + + const cleanUpComponentTemplates = () => + Promise.all(componentTemplatesCreated.map(removeComponentTemplate)) + .then(() => { + componentTemplatesCreated = []; + }) + .catch((err) => { + log.debug(`[Cleanup error] Error deleting ES resources: ${err.message}`); + }); + + const cleanUpIndexTemplates = () => + Promise.all(indexTemplatesCreated.map(removeIndexTemplate)) + .then(() => { + indexTemplatesCreated = []; + }) + .catch((err) => { + log.debug(`[Cleanup error] Error deleting ES resources: ${err.message}`); + }); + + const cleanupDatastreams = () => + Promise.all(datastreamCreated.map(removeDatastream)) + .then(() => { + datastreamCreated = []; + }) + .catch((err) => { + log.debug(`[Cleanup error] Error deleting ES resources: ${err.message}`); + }); + + return { + addDatastream, + addIndexTemplate, + addComponentTemplate, + + removeDatastream, + removeIndexTemplate, + removeComponentTemplate, + + cleanupDatastreams, + cleanUpIndexTemplates, + cleanUpComponentTemplates, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/component_templates.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/component_templates.api.ts new file mode 100644 index 0000000000000..60f604159e33c --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/component_templates.api.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +import { API_BASE_PATH } from '../constants'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +type Options = Partial | { _kbnMeta: Record }; + +export function componentTemplatesApi(getService: FtrProviderContext['getService']) { + const supertest = getService('supertest'); + + const createComponentTemplate = (name: string, options: Options) => + supertest + .post(`${API_BASE_PATH}/component_templates`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({ name, ...options }); + + const getAllComponentTemplates = () => + supertest + .get(`${API_BASE_PATH}/component_templates`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + const getOneComponentTemplate = (name: string) => + supertest + .get(`${API_BASE_PATH}/component_templates/${name}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + const updateComponentTemplate = (name: string, options: Options) => + supertest + .put(`${API_BASE_PATH}/component_templates/${name}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({ + name, + ...options, + }); + + const deleteComponentTemplate = (name: string) => + supertest + .delete(`${API_BASE_PATH}/component_templates/${name}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + const getComponentTemplateDatastreams = (name: string) => + supertest + .get(`${API_BASE_PATH}/component_templates/${name}/datastreams`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + return { + createComponentTemplate, + getAllComponentTemplates, + getOneComponentTemplate, + updateComponentTemplate, + deleteComponentTemplate, + getComponentTemplateDatastreams, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.ts new file mode 100644 index 0000000000000..65e2d733dd696 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/datastreams.helpers.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function datastreamsHelpers(getService: FtrProviderContext['getService']) { + const es = getService('es'); + + const createDataStream = async (name: string) => { + // A data stream requires an index template before it can be created. + await es.indices.putIndexTemplate({ + name, + body: { + // We need to match the names of backing indices with this template. + index_patterns: [name + '*'], + template: { + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + }, + }, + lifecycle: { + // @ts-expect-error @elastic/elasticsearch enabled prop is not typed yet + enabled: true, + }, + }, + data_stream: {}, + }, + }); + + await es.indices.createDataStream({ name }); + }; + + const updateIndexTemplateMappings = async (name: string, mappings: any) => { + await es.indices.putIndexTemplate({ + name, + body: { + // We need to match the names of backing indices with this template. + index_patterns: [name + '*'], + template: { + mappings, + }, + data_stream: {}, + }, + }); + }; + + const getDatastream = async (name: string) => { + const { + data_streams: [datastream], + } = await es.indices.getDataStream({ name }); + return datastream; + }; + + const getMapping = async (name: string) => { + const res = await es.indices.getMapping({ index: name }); + + return Object.values(res)[0]!.mappings; + }; + + const deleteComposableIndexTemplate = async (name: string) => { + await es.indices.deleteIndexTemplate({ name }); + }; + + const deleteDataStream = async (name: string) => { + await es.indices.deleteDataStream({ name }); + await deleteComposableIndexTemplate(name); + }; + + const assertDataStreamStorageSizeExists = (storageSize: string, storageSizeBytes: number) => { + // Storage size of a document doesn't look like it would be deterministic (could vary depending + // on how ES, Lucene, and the file system interact), so we'll just assert its presence and + // type. + expect(storageSize).to.be.ok(); + expect(typeof storageSize).to.be('string'); + expect(storageSizeBytes).to.be.ok(); + expect(typeof storageSizeBytes).to.be('number'); + }; + + return { + createDataStream, + updateIndexTemplateMappings, + getDatastream, + getMapping, + deleteComposableIndexTemplate, + deleteDataStream, + assertDataStreamStorageSizeExists, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/elasticsearch.js b/x-pack/test/api_integration/apis/management/index_management/lib/elasticsearch.js index d71085439e868..afad590d7ab6f 100644 --- a/x-pack/test/api_integration/apis/management/index_management/lib/elasticsearch.js +++ b/x-pack/test/api_integration/apis/management/index_management/lib/elasticsearch.js @@ -5,8 +5,6 @@ * 2.0. */ -import { getRandomString } from './random'; - /** * Helpers to create and delete indices on the Elasticsearch instance * during our tests. @@ -14,109 +12,10 @@ import { getRandomString } from './random'; */ export const initElasticsearchHelpers = (getService) => { const es = getService('es'); - const esDeleteAllIndices = getService('esDeleteAllIndices'); - - let indicesCreated = []; - let datastreamCreated = []; - let indexTemplatesCreated = []; - let componentTemplatesCreated = []; - - const createDatastream = (datastream) => { - datastreamCreated.push(datastream); - return es.indices.createDataStream({ name: datastream }); - }; - - const deleteDatastream = (datastream) => { - return es.indices.deleteDataStream({ name: datastream }); - }; - - const cleanupDatastreams = () => - Promise.all(datastreamCreated.map(deleteDatastream)) - .then(() => { - datastreamCreated = []; - }) - .catch((err) => { - // eslint-disable-next-line no-console - console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); - }); - - const createIndex = (index = getRandomString(), body) => { - indicesCreated.push(index); - return es.indices.create({ index, body }).then(() => index); - }; - - const deleteAllIndices = async () => { - await esDeleteAllIndices(indicesCreated); - indicesCreated = []; - }; - - const catIndex = (index, h) => es.cat.indices({ index, format: 'json', h }, { meta: true }); - - const indexStats = (index, metric) => es.indices.stats({ index, metric }, { meta: true }); - - const cleanUp = () => deleteAllIndices(); const catTemplate = (name) => es.cat.templates({ name, format: 'json' }, { meta: true }); - const createIndexTemplate = (indexTemplate, shouldCacheTemplate) => { - if (shouldCacheTemplate) { - indexTemplatesCreated.push(indexTemplate.name); - } - - return es.indices.putIndexTemplate(indexTemplate, { meta: true }); - }; - - const deleteIndexTemplate = (indexTemplateName) => { - return es.indices.deleteIndexTemplate({ name: indexTemplateName }, { meta: true }); - }; - - const createComponentTemplate = (componentTemplate, shouldCacheTemplate) => { - if (shouldCacheTemplate) { - componentTemplatesCreated.push(componentTemplate.name); - } - - return es.cluster.putComponentTemplate(componentTemplate, { meta: true }); - }; - - const deleteComponentTemplate = (componentTemplateName) => { - return es.cluster.deleteComponentTemplate({ name: componentTemplateName }, { meta: true }); - }; - - const cleanUpComponentTemplates = () => - Promise.all(componentTemplatesCreated.map(deleteComponentTemplate)) - .then(() => { - componentTemplatesCreated = []; - }) - .catch((err) => { - // eslint-disable-next-line no-console - console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); - }); - - const cleanUpIndexTemplates = () => - Promise.all(indexTemplatesCreated.map(deleteIndexTemplate)) - .then(() => { - indexTemplatesCreated = []; - }) - .catch((err) => { - // eslint-disable-next-line no-console - console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); - }); - return { - createIndex, - deleteAllIndices, - catIndex, - indexStats, - createDatastream, - deleteDatastream, - cleanupDatastreams, - cleanUp, catTemplate, - createIndexTemplate, - deleteIndexTemplate, - cleanUpIndexTemplates, - createComponentTemplate, - deleteComponentTemplate, - cleanUpComponentTemplates, }; }; diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/indices.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/indices.api.ts new file mode 100644 index 0000000000000..d75d3ec0527b6 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/indices.api.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { API_BASE_PATH } from '../constants'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function indicesApi(getService: FtrProviderContext['getService']) { + const supertest = getService('supertest'); + const executeActionOnIndices = ({ + index, + urlParam, + args, + }: { + index?: string | string[]; + urlParam: string; + args?: any; + }) => { + const indices = Array.isArray(index) ? index : [index]; + + return supertest + .post(`${API_BASE_PATH}/indices/${urlParam}`) + .set('kbn-xsrf', 'xxx') + .send({ indices, ...args }); + }; + + const closeIndex = (index: string) => executeActionOnIndices({ index, urlParam: 'close' }); + + const openIndex = (index: string) => executeActionOnIndices({ index, urlParam: 'open' }); + + const deleteIndex = (index?: string) => executeActionOnIndices({ index, urlParam: 'delete' }); + + const flushIndex = (index: string) => executeActionOnIndices({ index, urlParam: 'flush' }); + + const refreshIndex = (index: string) => executeActionOnIndices({ index, urlParam: 'refresh' }); + + const forceMerge = (index: string, args?: { maxNumSegments: number }) => + executeActionOnIndices({ index, urlParam: 'forcemerge', args }); + + const unfreeze = (index: string) => executeActionOnIndices({ index, urlParam: 'unfreeze' }); + + const clearCache = (index: string) => executeActionOnIndices({ index, urlParam: 'clear_cache' }); + + const list = () => supertest.get(`${API_BASE_PATH}/indices`); + + const reload = (indexNames?: string[]) => + supertest.post(`${API_BASE_PATH}/indices/reload`).set('kbn-xsrf', 'xxx').send({ indexNames }); + + return { + closeIndex, + openIndex, + deleteIndex, + flushIndex, + refreshIndex, + forceMerge, + unfreeze, + list, + reload, + clearCache, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/indices.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/lib/indices.helpers.ts new file mode 100644 index 0000000000000..139e813d84ffe --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/indices.helpers.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getRandomString } from './random'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function indicesHelpers(getService: FtrProviderContext['getService']) { + const es = getService('es'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + + let indicesCreated: string[] = []; + + const createIndex = async (index: string = getRandomString(), mappings?: any) => { + indicesCreated.push(index); + await es.indices.create({ index, mappings }); + return index; + }; + + const deleteAllIndices = async () => { + await esDeleteAllIndices(indicesCreated); + indicesCreated = []; + }; + + const catIndex = (index?: string, h?: any) => + es.cat.indices({ index, format: 'json', h }, { meta: true }); + + const indexStats = (index: string, metric: string) => + es.indices.stats({ index, metric }, { meta: true }); + + return { createIndex, deleteAllIndices, catIndex, indexStats }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/mappings.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/mappings.api.ts new file mode 100644 index 0000000000000..f032f2893e318 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/mappings.api.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 { API_BASE_PATH } from '../constants'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function mappingsApi(getService: FtrProviderContext['getService']) { + const supertest = getService('supertest'); + + const getMapping = (index: string) => + supertest + .get(`${API_BASE_PATH}/mapping/${index}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + return { + getMapping, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/random.js b/x-pack/test/api_integration/apis/management/index_management/lib/random.ts similarity index 100% rename from x-pack/test/api_integration/apis/management/index_management/lib/random.js rename to x-pack/test/api_integration/apis/management/index_management/lib/random.ts diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/settings.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/settings.api.ts new file mode 100644 index 0000000000000..67b3c275775c4 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/lib/settings.api.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IndexSettings } from '@kbn/index-management-plugin/common'; + +import { API_BASE_PATH } from '../constants'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export function settingsApi(getService: FtrProviderContext['getService']) { + const supertest = getService('supertest'); + + const getIndexSettings = (index: string) => + supertest + .get(`${API_BASE_PATH}/settings/${index}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx'); + + const updateIndexSettings = (index: string, settings: IndexSettings) => + supertest + .put(`${API_BASE_PATH}/settings/${index}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send(settings); + + return { + getIndexSettings, + updateIndexSettings, + }; +} diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.js b/x-pack/test/api_integration/apis/management/index_management/mapping.js deleted file mode 100644 index c18bc4c163526..0000000000000 --- a/x-pack/test/api_integration/apis/management/index_management/mapping.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; - -import { initElasticsearchHelpers } from './lib'; -import { registerHelpers } from './mapping.helpers'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - const { createIndex, cleanUp: cleanUpEsResources } = initElasticsearchHelpers(getService); - - const { getIndexMapping } = registerHelpers({ supertest }); - - describe('mapping', () => { - after(() => Promise.all([cleanUpEsResources()])); - - it('should fetch the index mapping', async () => { - const mappings = { - properties: { - total: { type: 'long' }, - tag: { type: 'keyword' }, - createdAt: { type: 'date' }, - }, - }; - const index = await createIndex(undefined, { mappings }); - - const { body } = await getIndexMapping(index).expect(200); - - expect(body.mappings).to.eql(mappings); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.ts b/x-pack/test/api_integration/apis/management/index_management/mapping.ts new file mode 100644 index 0000000000000..5e3ebea13adc6 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/mapping.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { mappingsApi } from './lib/mappings.api'; +import { indicesHelpers } from './lib/indices.helpers'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); + + const { getMapping } = mappingsApi(getService); + const { createIndex, deleteAllIndices } = indicesHelpers(getService); + + describe('mappings', () => { + let indexName: string; + + const mappings = { + properties: { + total: { type: 'long' }, + tag: { type: 'keyword' }, + createdAt: { type: 'date' }, + }, + }; + + after(async () => await deleteAllIndices()); + + before(async () => { + log.debug('Creating index'); + try { + indexName = await createIndex(undefined, mappings); + } catch (err) { + log.debug('[Setup error] Error creating index'); + throw err; + } + }); + + after(async () => { + try { + await deleteAllIndices(); + } catch (err) { + log.debug('[Cleanup error] Error deleting index'); + throw err; + } + }); + + it('should get the index mappings', async () => { + const { body } = await getMapping(indexName).expect(200); + + expect(body.mappings).to.eql(mappings); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/management/index_management/settings.helpers.js b/x-pack/test/api_integration/apis/management/index_management/settings.helpers.js deleted file mode 100644 index 58573b1572fa8..0000000000000 --- a/x-pack/test/api_integration/apis/management/index_management/settings.helpers.js +++ /dev/null @@ -1,20 +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 { API_BASE_PATH } from './constants'; - -export const registerHelpers = ({ supertest }) => { - const getIndexSettings = (indexName) => supertest.get(`${API_BASE_PATH}/settings/${indexName}`); - - const updateIndexSettings = (indexName, settings) => - supertest.put(`${API_BASE_PATH}/settings/${indexName}`).set('kbn-xsrf', 'xxx').send(settings); - - return { - getIndexSettings, - updateIndexSettings, - }; -}; diff --git a/x-pack/test/api_integration/apis/management/index_management/settings.js b/x-pack/test/api_integration/apis/management/index_management/settings.ts similarity index 86% rename from x-pack/test/api_integration/apis/management/index_management/settings.js rename to x-pack/test/api_integration/apis/management/index_management/settings.ts index 44ee80aaec7a2..573a0374e9c25 100644 --- a/x-pack/test/api_integration/apis/management/index_management/settings.js +++ b/x-pack/test/api_integration/apis/management/index_management/settings.ts @@ -7,18 +7,16 @@ import expect from '@kbn/expect'; -import { initElasticsearchHelpers } from './lib'; -import { registerHelpers } from './settings.helpers'; +import { indicesHelpers } from './lib/indices.helpers'; +import { settingsApi } from './lib/settings.api'; +import { FtrProviderContext } from '../../../ftr_provider_context'; -export default function ({ getService }) { - const supertest = getService('supertest'); - - const { createIndex, cleanUp: cleanUpEsResources } = initElasticsearchHelpers(getService); - - const { getIndexSettings, updateIndexSettings } = registerHelpers({ supertest }); +export default function ({ getService }: FtrProviderContext) { + const { createIndex, deleteAllIndices } = indicesHelpers(getService); + const { getIndexSettings, updateIndexSettings } = settingsApi(getService); describe('settings', () => { - after(() => Promise.all([cleanUpEsResources()])); + after(async () => await deleteAllIndices()); it('should fetch an index settings', async () => { const index = await createIndex(); diff --git a/x-pack/test/api_integration/apis/management/index_management/stats.js b/x-pack/test/api_integration/apis/management/index_management/stats.js index c673f10461715..782b32a787323 100644 --- a/x-pack/test/api_integration/apis/management/index_management/stats.js +++ b/x-pack/test/api_integration/apis/management/index_management/stats.js @@ -7,18 +7,18 @@ import expect from '@kbn/expect'; -import { initElasticsearchHelpers } from './lib'; +import { indicesHelpers } from './lib/indices.helpers'; import { registerHelpers } from './stats.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const { createIndex, cleanUp: cleanUpEsResources } = initElasticsearchHelpers(getService); + const { createIndex, deleteAllIndices } = indicesHelpers(getService); const { getIndexStats } = registerHelpers({ supertest }); describe('stats', () => { - after(() => Promise.all([cleanUpEsResources()])); + after(async () => await deleteAllIndices()); it('should fetch the index stats', async () => { const index = await createIndex(); diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.js b/x-pack/test/api_integration/apis/management/index_management/templates.js index bb4007852b037..4882ecccf6346 100644 --- a/x-pack/test/api_integration/apis/management/index_management/templates.js +++ b/x-pack/test/api_integration/apis/management/index_management/templates.js @@ -13,7 +13,7 @@ import { registerHelpers } from './templates.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const { cleanUp: cleanUpEsResources, catTemplate } = initElasticsearchHelpers(getService); + const { catTemplate } = initElasticsearchHelpers(getService); const { getAllTemplates, @@ -27,7 +27,7 @@ export default function ({ getService }) { // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/170980 describe.skip('index templates', () => { - after(() => Promise.all([cleanUpEsResources(), cleanUpTemplates()])); + after(async () => await cleanUpTemplates()); describe('get all', () => { const indexTemplate = getTemplatePayload(`template-${getRandomString()}`, [ diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/create_job.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/create_job.ts index 84e4f03cdc130..b344579645bfe 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/create_job.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/create_job.ts @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; -import type { DataFrameAnalyticsConfig } from '@kbn/ml-data-frame-analytics-utils'; +import { + type DataFrameAnalyticsConfig, + ANALYSIS_CONFIG_TYPE, +} from '@kbn/ml-data-frame-analytics-utils'; import { DeepPartial } from '@kbn/ml-plugin/common/types/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../../functional/services/ml/security_common'; @@ -35,8 +38,9 @@ export default ({ getService }: FtrProviderContext) => { max_num_threads: 1, // default value }; - const jobTypes = ['classification', 'regression', 'outlier_detection']; - const jobAnalyses: any = { + const jobTypes = Object.values(ANALYSIS_CONFIG_TYPE); + type JobType = typeof jobTypes[number]; + const jobAnalyses = { classification: { dependent_variable: 'y', training_percent: 20, @@ -53,7 +57,7 @@ export default ({ getService }: FtrProviderContext) => { const testJobConfigs: Array<{ jobId: string; - jobType: string; + jobType: JobType; config: DeepPartial; }> = ['Test classification job', 'Test regression job', 'Test outlier detection job'].map( (description, idx) => { @@ -100,12 +104,55 @@ export default ({ getService }: FtrProviderContext) => { expect(body).not.to.be(undefined); - expect(body.description).to.eql(requestBody.description); - expect(body.allow_lazy_start).to.eql(requestBody.allow_lazy_start); - expect(body.model_memory_limit).to.eql(requestBody.model_memory_limit); - expect(body.max_num_threads).to.eql(requestBody.max_num_threads); + expect(body.dataFrameAnalyticsJobsCreated).to.have.length( + 1, + `Expected dataFrameAnalyticsJobsCreated length to be 1, got ${body.dataFrameAnalyticsJobsCreated}.` + ); + expect(body.dataFrameAnalyticsJobsErrors).to.have.length( + 0, + `Expected dataFrameAnalyticsJobsErrors length to be 0, got ${body.dataFrameAnalyticsJobsErrors}.` + ); + expect(body.dataViewsCreated).to.have.length( + 0, + `Expected dataViewsCreated length to be 0, got ${body.dataViewsCreated}.` + ); + expect(body.dataViewsErrors).to.have.length( + 0, + `Expected dataViewsErrors length to be 0, got ${body.dataViewsErrors}.` + ); + }); + + it(`should create ${testConfig.jobType} job and data view with given config`, async () => { + const analyticsId = `${testConfig.jobId}_with_data_view`; + const requestBody = testConfig.config; + + const { body, status } = await supertest + .put( + `/internal/ml/data_frame/analytics/${analyticsId}?createDataView=true&timeFieldName=@timestamp` + ) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(getCommonRequestHeader('1')) + .send(requestBody); + ml.api.assertResponseStatusCode(200, status, body); + + expect(body).not.to.be(undefined); - expect(Object.keys(body.analysis)).to.eql(Object.keys(requestBody.analysis!)); + expect(body.dataFrameAnalyticsJobsCreated).to.have.length( + 1, + `Expected dataFrameAnalyticsJobsCreated length to be 1, got ${body.dataFrameAnalyticsJobsCreated}.` + ); + expect(body.dataFrameAnalyticsJobsErrors).to.have.length( + 0, + `Expected dataFrameAnalyticsJobsErrors length to be 0, got ${body.dataFrameAnalyticsJobsErrors}.` + ); + expect(body.dataViewsCreated).to.have.length( + 1, + `Expected dataViewsCreated length to be 1, got ${body.dataViewsCreated}.` + ); + expect(body.dataViewsErrors).to.have.length( + 0, + `Expected dataViewsErrors length to be 0, got ${body.dataViewsErrors}.` + ); }); }); diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts index f0e69eddb9811..f7e3d16666342 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts @@ -42,8 +42,8 @@ export default ({ getService }: FtrProviderContext) => { const testJobConfigs: Array> = [ 'Test delete job only', 'Test delete job and target index', - 'Test delete job and index pattern', - 'Test delete job, target index, and index pattern', + 'Test delete job and data view', + 'Test delete job, target index, and data view', ].map((description, idx) => { const analyticsId = `${jobId}_${idx + 1}`; return { @@ -148,71 +148,71 @@ export default ({ getService }: FtrProviderContext) => { expect(body.analyticsJobDeleted.success).to.eql(true); expect(body.destIndexDeleted.success).to.eql(true); - expect(body.destIndexPatternDeleted.success).to.eql(false); + expect(body.destDataViewDeleted.success).to.eql(false); await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); await ml.api.assertIndicesNotToExist(destinationIndex); }); }); - describe('with deleteDestIndexPattern setting', function () { + describe('with deleteDestDataView setting', function () { const analyticsId = `${jobId}_3`; const destinationIndex = generateDestinationIndex(analyticsId); before(async () => { - // Mimic real job by creating index pattern after job is created - await ml.testResources.createIndexPatternIfNeeded(destinationIndex); + // Mimic real job by creating data view after job is created + await ml.testResources.createDataViewIfNeeded(destinationIndex); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle(destinationIndex); + await ml.testResources.deleteDataViewByTitle(destinationIndex); }); - it('should delete job and index pattern by id', async () => { + it('should delete job and data view by id', async () => { const { body, status } = await supertest .delete(`/internal/ml/data_frame/analytics/${analyticsId}`) - .query({ deleteDestIndexPattern: true }) + .query({ deleteDestDataView: true }) .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) .set(getCommonRequestHeader('1')); ml.api.assertResponseStatusCode(200, status, body); expect(body.analyticsJobDeleted.success).to.eql(true); expect(body.destIndexDeleted.success).to.eql(false); - expect(body.destIndexPatternDeleted.success).to.eql(true); + expect(body.destDataViewDeleted.success).to.eql(true); await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); - await ml.testResources.assertIndexPatternNotExist(destinationIndex); + await ml.testResources.assertDataViewNotExist(destinationIndex); }); }); - describe('with deleteDestIndex & deleteDestIndexPattern setting', function () { + describe('with deleteDestIndex & deleteDestDataView setting', function () { const analyticsId = `${jobId}_4`; const destinationIndex = generateDestinationIndex(analyticsId); before(async () => { - // Mimic real job by creating target index & index pattern after DFA job is created + // Mimic real job by creating target index & data view after DFA job is created await ml.api.createIndex(destinationIndex); await ml.api.assertIndicesExist(destinationIndex); - await ml.testResources.createIndexPatternIfNeeded(destinationIndex); + await ml.testResources.createDataViewIfNeeded(destinationIndex); }); after(async () => { await ml.api.deleteIndices(destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(destinationIndex); + await ml.testResources.deleteDataViewByTitle(destinationIndex); }); - it('should delete job, target index, and index pattern by id', async () => { + it('should delete job, target index, and data view by id', async () => { const { body, status } = await supertest .delete(`/internal/ml/data_frame/analytics/${analyticsId}`) - .query({ deleteDestIndex: true, deleteDestIndexPattern: true }) + .query({ deleteDestIndex: true, deleteDestDataView: true }) .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) .set(getCommonRequestHeader('1')); ml.api.assertResponseStatusCode(200, status, body); expect(body.analyticsJobDeleted.success).to.eql(true); expect(body.destIndexDeleted.success).to.eql(true); - expect(body.destIndexPatternDeleted.success).to.eql(true); + expect(body.destDataViewDeleted.success).to.eql(true); await ml.api.waitForDataFrameAnalyticsJobNotToExist(analyticsId); await ml.api.assertIndicesNotToExist(destinationIndex); - await ml.testResources.assertIndexPatternNotExist(destinationIndex); + await ml.testResources.assertDataViewNotExist(destinationIndex); }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts index 464014f99648e..2459f81b188b7 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts @@ -273,9 +273,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body.elements.length).to.eql(0); expect(body.details).to.eql({}); - expect(body.error).to.eql(`No known job with id '${jobId}_fake'`); - - expect(body).to.have.keys('elements', 'details', 'error'); + expect(body).to.have.keys('elements', 'details'); }); }); diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts index e294ddb51d7ea..4b981b14a381c 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get_spaces.ts @@ -207,7 +207,6 @@ export default ({ getService }: FtrProviderContext) => { `Expected 0 map elements, got ${body.elements.length}` ); expect(body.details).to.eql({}); - expect(body.error).to.eql(`No known job with id '${jobIdSpace1}'`); }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts b/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts index 4f759a1c5a932..eff763fbcfcdd 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts @@ -46,12 +46,12 @@ export default ({ getService }: FtrProviderContext) => { describe('close_jobs', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); beforeEach(async () => { diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts index f45f7052b65c6..610d5be812785 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts @@ -83,13 +83,13 @@ export default ({ getService }: FtrProviderContext) => { describe('jobs_exist', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); }); after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('sets up jobs', async () => { diff --git a/x-pack/test/api_integration/apis/ml/jobs/reset.ts b/x-pack/test/api_integration/apis/ml/jobs/reset.ts index 0b69097a907e9..2e193816efb90 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/reset.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/reset.ts @@ -55,12 +55,12 @@ export default ({ getService }: FtrProviderContext) => { describe('reset_jobs', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); beforeEach(async () => { diff --git a/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts b/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts index 0f59baa11d137..22176a6e5c7f0 100644 --- a/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts +++ b/x-pack/test/api_integration/apis/ml/modules/jobs_exist.ts @@ -39,12 +39,12 @@ export default ({ getService }: FtrProviderContext) => { await ml.testResources.setKibanaTimeZoneToUTC(); await esArchiver.loadIfNeeded(sourceDataArchive); // create data view in default space - await ml.testResources.createIndexPatternIfNeeded( + await ml.testResources.createDataViewIfNeeded( moduleInfo.dataView.name, moduleInfo.dataView.timeField ); // create data view in idSpace1 - await ml.testResources.createIndexPatternIfNeeded( + await ml.testResources.createDataViewIfNeeded( moduleInfo.dataView.name, moduleInfo.dataView.timeField, idSpace1 @@ -57,8 +57,8 @@ export default ({ getService }: FtrProviderContext) => { after(async () => { // delete all data views in all spaces - await ml.testResources.deleteIndexPatternByTitle(moduleInfo.dataView.name); - await ml.testResources.deleteIndexPatternByTitle(moduleInfo.dataView.name, idSpace1); + await ml.testResources.deleteDataViewByTitle(moduleInfo.dataView.name); + await ml.testResources.deleteDataViewByTitle(moduleInfo.dataView.name, idSpace1); }); it('should find jobs installed by module without prefix', async () => { diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts index 1eb327c897b08..27d6d048e3f84 100644 --- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -710,7 +710,7 @@ export default ({ getService }: FtrProviderContext) => { describe('sets up module data', function () { before(async () => { await esArchiver.loadIfNeeded(testData.sourceDataArchive); - await ml.testResources.createIndexPatternIfNeeded( + await ml.testResources.createDataViewIfNeeded( testData.indexPattern.name, testData.indexPattern.timeField ); @@ -730,7 +730,7 @@ export default ({ getService }: FtrProviderContext) => { await ml.api.deleteAnomalyDetectionJobES(job.jobId); } await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle(testData.indexPattern.name); + await ml.testResources.deleteDataViewByTitle(testData.indexPattern.name); }); it(testData.testTitleSuffix, async () => { @@ -864,7 +864,7 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.loadIfNeeded(testData.sourceDataArchive!); } if (testData.hasOwnProperty('indexPattern')) { - await ml.testResources.createIndexPatternIfNeeded( + await ml.testResources.createDataViewIfNeeded( testData.indexPattern!.name as string, testData.indexPattern!.timeField as string ); @@ -874,7 +874,7 @@ export default ({ getService }: FtrProviderContext) => { after(async () => { await ml.api.cleanMlIndices(); if (testData.hasOwnProperty('indexPattern')) { - await ml.testResources.deleteIndexPatternByTitle(testData.indexPattern!.name); + await ml.testResources.deleteDataViewByTitle(testData.indexPattern!.name); } }); diff --git a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts index 9c0add9e6be9c..87154beb07efe 100644 --- a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts +++ b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_logs'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); await ml.api.cleanMlIndices(); }); diff --git a/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts b/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts index d722141282328..3dc4686102c3d 100644 --- a/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts +++ b/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_logs'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); await ml.api.cleanMlIndices(); }); diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 81cceb6561bd6..04a4177485348 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -22,8 +22,22 @@ export default function ({ getService }: FtrProviderContext) { savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'], canvas: ['all', 'read', 'minimal_all', 'minimal_read'], maps: ['all', 'read', 'minimal_all', 'minimal_read'], - generalCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], - observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + generalCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], + observabilityCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], observabilityAIAssistant: ['all', 'read', 'minimal_all', 'minimal_read'], slo: ['all', 'read', 'minimal_all', 'minimal_read'], fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'], @@ -57,7 +71,14 @@ export default function ({ getService }: FtrProviderContext) { ], uptime: ['all', 'read', 'minimal_all', 'minimal_read', 'elastic_managed_locations_enabled'], securitySolutionAssistant: ['all', 'read', 'minimal_all', 'minimal_read'], - securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + securitySolutionCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'], logs: ['all', 'read', 'minimal_all', 'minimal_read'], apm: ['all', 'read', 'minimal_all', 'minimal_read'], diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 174ac2a3c8f66..2773adfe070e8 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -98,8 +98,22 @@ export default function ({ getService }: FtrProviderContext) { savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'], canvas: ['all', 'read', 'minimal_all', 'minimal_read'], maps: ['all', 'read', 'minimal_all', 'minimal_read'], - generalCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], - observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + generalCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], + observabilityCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], observabilityAIAssistant: ['all', 'read', 'minimal_all', 'minimal_read'], slo: ['all', 'read', 'minimal_all', 'minimal_read'], fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'], @@ -139,7 +153,14 @@ export default function ({ getService }: FtrProviderContext) { 'minimal_read', ], securitySolutionAssistant: ['all', 'read', 'minimal_all', 'minimal_read'], - securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'], + securitySolutionCases: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'cases_delete', + 'cases_settings', + ], infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'], logs: ['all', 'read', 'minimal_all', 'minimal_read'], apm: ['all', 'read', 'minimal_all', 'minimal_read'], diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor.ts index db98633941eee..72e872de27f99 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor.ts @@ -9,7 +9,11 @@ import moment from 'moment/moment'; import { v4 as uuidv4 } from 'uuid'; import { omit } from 'lodash'; import { secretKeys } from '@kbn/synthetics-plugin/common/constants/monitor_management'; -import { ConfigKey, DataStream, HTTPFields } from '@kbn/synthetics-plugin/common/runtime_types'; +import { + ConfigKey, + MonitorTypeEnum, + HTTPFields, +} from '@kbn/synthetics-plugin/common/runtime_types'; import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters'; import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants'; import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults'; @@ -124,7 +128,7 @@ export default function ({ getService }: FtrProviderContext) { expect(apiResponse.body).eql( omit( { - ...DEFAULT_FIELDS[DataStream.HTTP], + ...DEFAULT_FIELDS[MonitorTypeEnum.HTTP], ...newMonitor, [ConfigKey.MONITOR_QUERY_ID]: apiResponse.body.id, [ConfigKey.CONFIG_ID]: apiResponse.body.id, diff --git a/x-pack/test/api_integration/apis/transform/delete_transforms.ts b/x-pack/test/api_integration/apis/transform/delete_transforms.ts index faf18e5d610e3..a064fd28b214b 100644 --- a/x-pack/test/api_integration/apis/transform/delete_transforms.ts +++ b/x-pack/test/api_integration/apis/transform/delete_transforms.ts @@ -232,12 +232,12 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await createTransform(transformId); await transform.api.createIndices(destinationIndex); - await transform.testResources.createIndexPatternIfNeeded(destinationIndex); + await transform.testResources.createDataViewIfNeeded(destinationIndex); }); after(async () => { await transform.api.deleteIndices(destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(destinationIndex); + await transform.testResources.deleteDataViewByTitle(destinationIndex); }); it('should delete transform and destination index pattern', async () => { @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body[transformId].destDataViewDeleted.success).to.eql(true); await transform.api.waitForTransformNotToExist(transformId); await transform.api.waitForIndicesToExist(destinationIndex); - await transform.testResources.assertIndexPatternNotExist(destinationIndex); + await transform.testResources.assertDataViewNotExist(destinationIndex); }); }); @@ -272,12 +272,12 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await createTransform(transformId); await transform.api.createIndices(destinationIndex); - await transform.testResources.createIndexPatternIfNeeded(destinationIndex); + await transform.testResources.createDataViewIfNeeded(destinationIndex); }); after(async () => { await transform.api.deleteIndices(destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(destinationIndex); + await transform.testResources.deleteDataViewByTitle(destinationIndex); }); it('should delete transform, destination index, & destination index pattern', async () => { @@ -301,7 +301,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body[transformId].destDataViewDeleted.success).to.eql(true); await transform.api.waitForTransformNotToExist(transformId); await transform.api.waitForIndicesNotToExist(destinationIndex); - await transform.testResources.assertIndexPatternNotExist(destinationIndex); + await transform.testResources.assertDataViewNotExist(destinationIndex); }); }); }); diff --git a/x-pack/test/api_integration/apis/transform/transforms_create.ts b/x-pack/test/api_integration/apis/transform/transforms_create.ts index 84617f6990e3e..a41a7f3c57093 100644 --- a/x-pack/test/api_integration/apis/transform/transforms_create.ts +++ b/x-pack/test/api_integration/apis/transform/transforms_create.ts @@ -84,7 +84,7 @@ export default ({ getService }: FtrProviderContext) => { }, ]); - await transform.testResources.deleteIndexPatternByTitle(destinationIndex); + await transform.testResources.deleteDataViewByTitle(destinationIndex); }); it('should create a transform with data view and time field', async () => { @@ -118,7 +118,7 @@ export default ({ getService }: FtrProviderContext) => { }, ]); - await transform.testResources.deleteIndexPatternByTitle(destinationIndex); + await transform.testResources.deleteDataViewByTitle(destinationIndex); }); it('should not allow pivot and latest configs in same transform', async () => { diff --git a/x-pack/test/api_integration/apis/watcher/watcher.ts b/x-pack/test/api_integration/apis/watcher/watcher.ts index 734b6c8c6212d..a5d26c98dfe73 100644 --- a/x-pack/test/api_integration/apis/watcher/watcher.ts +++ b/x-pack/test/api_integration/apis/watcher/watcher.ts @@ -17,7 +17,7 @@ export default function ({ getService }: FtrProviderContext) { describe('watcher', () => { before(async () => { try { - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); } catch (error) { log.debug('[Setup error] Error creating index pattern'); throw error; @@ -26,7 +26,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { try { - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); } catch (error) { log.debug('[Cleanup error] Error deleting index pattern'); throw error; diff --git a/x-pack/test/api_integration/services/index.ts b/x-pack/test/api_integration/services/index.ts index 6ef3e393a86e6..1f8d4576d908d 100644 --- a/x-pack/test/api_integration/services/index.ts +++ b/x-pack/test/api_integration/services/index.ts @@ -21,6 +21,7 @@ import { MachineLearningProvider } from './ml'; import { IngestManagerProvider } from '../../common/services/ingest_manager'; import { TransformProvider } from './transform'; import { IngestPipelinesProvider } from './ingest_pipelines'; +import { IndexManagementProvider } from './index_management'; export const services = { ...commonServices, @@ -37,4 +38,5 @@ export const services = { ingestManager: IngestManagerProvider, transform: TransformProvider, ingestPipelines: IngestPipelinesProvider, + indexManagement: IndexManagementProvider, }; diff --git a/x-pack/test/api_integration/services/index_management.ts b/x-pack/test/api_integration/services/index_management.ts new file mode 100644 index 0000000000000..98d0b8b739148 --- /dev/null +++ b/x-pack/test/api_integration/services/index_management.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; +import { indicesApi } from '../apis/management/index_management/lib/indices.api'; +import { mappingsApi } from '../apis/management/index_management/lib/mappings.api'; +import { indicesHelpers } from '../apis/management/index_management/lib/indices.helpers'; +import { componentTemplatesApi } from '../apis/management/index_management/lib/component_templates.api'; +import { componentTemplateHelpers } from '../apis/management/index_management/lib/component_template.helpers'; +import { settingsApi } from '../apis/management/index_management/lib/settings.api'; +import { clusterNodesApi } from '../apis/management/index_management/lib/cluster_nodes.api'; +import { datastreamsHelpers } from '../apis/management/index_management/lib/datastreams.helpers'; + +export function IndexManagementProvider({ getService }: FtrProviderContext) { + return { + indices: { + api: indicesApi(getService), + helpers: indicesHelpers(getService), + }, + componentTemplates: { + api: componentTemplatesApi(getService), + helpers: componentTemplateHelpers(getService), + }, + clusterNodes: { + api: clusterNodesApi(getService), + }, + datastreams: { + helpers: datastreamsHelpers(getService), + }, + mappings: { + api: mappingsApi(getService), + }, + settings: { + api: settingsApi(getService), + }, + }; +} diff --git a/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts b/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts index c07d6dc6c0f5e..b37d68fe936af 100644 --- a/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts @@ -7,6 +7,8 @@ import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -29,6 +31,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { end: new Date(end).toISOString(), kuery: '', environment: 'production', + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, }, }, }); diff --git a/x-pack/test/cases_api_integration/common/plugins/security_solution/server/plugin.ts b/x-pack/test/cases_api_integration/common/plugins/security_solution/server/plugin.ts index 9648e89827568..3f46aa016811c 100644 --- a/x-pack/test/cases_api_integration/common/plugins/security_solution/server/plugin.ts +++ b/x-pack/test/cases_api_integration/common/plugins/security_solution/server/plugin.ts @@ -68,13 +68,13 @@ export class FixturePlugin implements Plugin { const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('find_cases', () => { - const secOnlyInfo: User = getUserInfo(secOnlySpacesAll); let cookies: Cookie[]; let suggestedSecUsers: UserProfile[]; let superUserHeaders: { Cookie: string }; @@ -62,7 +59,7 @@ export default ({ getService }: FtrProviderContext): void => { suggestedSecUsers = await suggestUserProfiles({ supertest: supertestWithoutAuth, req: { - name: secOnlyInfo.username!, + name: 'all_spaces', owners: ['securitySolutionFixture'], size: 1, }, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/suggest_user_profiles.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/suggest_user_profiles.ts index db3f529863f01..e8e7023c06937 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/suggest_user_profiles.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/internal/suggest_user_profiles.ts @@ -58,6 +58,30 @@ export default function ({ getService }: FtrProviderContext) { `); }); + it('find a user who only has read privilege for cases', async () => { + const profiles = await suggestUserProfiles({ + supertest: supertestWithoutAuth, + req: { + name: 'read', + owners: ['securitySolutionFixture'], + }, + auth: { user: superUser, space: 'space1' }, + }); + + expectSnapshot(profiles.map(({ user, data }) => ({ user, data }))).toMatchInline(` + Array [ + Object { + "data": Object {}, + "user": Object { + "email": "sec_only_read@elastic.co", + "full_name": "sec only_read", + "username": "sec_only_read", + }, + }, + ] + `); + }); + it('does not find a user who does not have access to the default space', async () => { const profiles = await suggestUserProfiles({ supertest: supertestWithoutAuth, @@ -85,6 +109,14 @@ export default function ({ getService }: FtrProviderContext) { expect(profiles.filter(({ user }) => user.username === obsOnly.username)).to.be.empty(); expectSnapshot(profiles.map(({ user, data }) => ({ user, data }))).toMatchInline(` Array [ + Object { + "data": Object {}, + "user": Object { + "email": "sec_only_read@elastic.co", + "full_name": "sec only_read", + "username": "sec_only_read", + }, + }, Object { "data": Object {}, "user": Object { @@ -105,19 +137,6 @@ export default function ({ getService }: FtrProviderContext) { `); }); - it('does not find a user who does not have update privileges to cases', async () => { - const profiles = await suggestUserProfiles({ - supertest: supertestWithoutAuth, - req: { - name: 'read', - owners: ['securitySolutionFixture'], - }, - auth: { user: superUser, space: 'space1' }, - }); - - expect(profiles).to.be.empty(); - }); - it('fails with a 403 because the user making the request does not have the appropriate api kibana endpoint privileges', async () => { await suggestUserProfiles({ supertest: supertestWithoutAuth, @@ -186,9 +205,9 @@ export default function ({ getService }: FtrProviderContext) { Object { "data": Object {}, "user": Object { - "email": "sec_only_no_delete@elastic.co", - "full_name": "sec only_no_delete", - "username": "sec_only_no_delete", + "email": "sec_only_read@elastic.co", + "full_name": "sec only_read", + "username": "sec_only_read", }, }, ] @@ -242,7 +261,7 @@ export default function ({ getService }: FtrProviderContext) { await deleteUsersAndRoles(getService, users, roles); }); - it('finds 3 profiles when searching for the name sec when a user has both security and observability privileges', async () => { + it('finds 4 profiles when searching for the name sec when a user has both security and observability privileges', async () => { const profiles = await suggestUserProfiles({ supertest: supertestWithoutAuth, req: { @@ -254,6 +273,14 @@ export default function ({ getService }: FtrProviderContext) { expectSnapshot(profiles.map(({ user, data }) => ({ user, data }))).toMatchInline(` Array [ + Object { + "data": Object {}, + "user": Object { + "email": "sec_only_read@elastic.co", + "full_name": "sec only_read", + "username": "sec_only_read", + }, + }, Object { "data": Object {}, "user": Object { diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts index 0fd0e463e7087..c711c2300e1be 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts @@ -410,6 +410,44 @@ export function FindingsPageProvider({ getService, getPageObjects }: FtrProvider }, }); + const groupSelector = (testSubj = 'group-selector-dropdown') => ({ + async getElement() { + return await testSubjects.find(testSubj); + }, + async setValue(value: string) { + const contextMenu = await testSubjects.find('groupByContextMenu'); + const menuItems = await contextMenu.findAllByCssSelector('button.euiContextMenuItem'); + const menuItemsOptions = await Promise.all(menuItems.map((item) => item.getVisibleText())); + const menuItemValueIndex = menuItemsOptions.findIndex((item) => item === value); + await menuItems[menuItemValueIndex].click(); + return await testSubjects.missingOrFail('is-loading-grouping-table', { timeout: 5000 }); + }, + async openDropDown() { + const element = await this.getElement(); + await element.click(); + }, + }); + + const findingsGrouping = async (testSubj = 'cloudSecurityGrouping') => ({ + async getElement() { + return await testSubjects.find(testSubj); + }, + async getGroupCount() { + const element = await this.getElement(); + const groupCount = await element.findByTestSubject('group-count'); + return await groupCount.getVisibleText(); + }, + async getUnitCount() { + const element = await this.getElement(); + const unitCount = await element.findByTestSubject('unit-count'); + return await unitCount.getVisibleText(); + }, + async getRowAtIndex(rowIndex: number) { + const element = await this.getElement(); + const row = await element.findAllByTestSubject('grouping-accordion'); + return await row[rowIndex]; + }, + }); const isLatestFindingsTableThere = async () => { const table = await testSubjects.findAll('docTable'); const trueOrFalse = table.length > 0 ? true : false; @@ -432,6 +470,9 @@ export function FindingsPageProvider({ getService, getPageObjects }: FtrProvider misconfigurationsFlyout, toastMessage, detectionRuleApi, + groupSelector, + findingsGrouping, + createDataTableObject, isLatestFindingsTableThere, }; } diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings.ts b/x-pack/test/cloud_security_posture_functional/pages/findings.ts index 4ec0240f735c9..bf45dddebc0b5 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings.ts @@ -13,7 +13,6 @@ import type { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const queryBar = getService('queryBar'); const filterBar = getService('filterBar'); - const comboBox = getService('comboBox'); const retry = getService('retry'); const pageObjects = getPageObjects(['common', 'findings', 'header']); const chance = new Chance(); @@ -95,24 +94,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const ruleName1 = data[0].rule.name; const ruleName2 = data[1].rule.name; - const resourceId1 = data[0].resource.id; - const ruleSection1 = data[0].rule.section; - - const benchMarkName = data[0].rule.benchmark.name; - describe('Findings Page', function () { this.tags(['cloud_security_posture_findings']); let findings: typeof pageObjects.findings; let latestFindingsTable: typeof findings.latestFindingsTable; - let findingsByResourceTable: typeof findings.findingsByResourceTable; - let resourceFindingsTable: typeof findings.resourceFindingsTable; let distributionBar: typeof findings.distributionBar; before(async () => { findings = pageObjects.findings; latestFindingsTable = findings.latestFindingsTable; - findingsByResourceTable = findings.findingsByResourceTable; - resourceFindingsTable = findings.resourceFindingsTable; distributionBar = findings.distributionBar; // Before we start any test we must wait for cloud_security_posture plugin to complete its initialization @@ -219,19 +209,5 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); }); - - describe('GroupBy', () => { - it('groups findings by resource', async () => { - await comboBox.set('findings_group_by_selector', 'Resource'); - expect( - await findingsByResourceTable.hasColumnValue('Applicable Benchmark', benchMarkName) - ).to.be(true); - }); - - it('navigates to resource findings page from resource id link', async () => { - await findingsByResourceTable.clickResourceIdLink(resourceId1, ruleSection1); - expect(await resourceFindingsTable.hasColumnValue('Rule Name', ruleName1)).to.be(true); - }); - }); }); } diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts index f346fc3d5c285..9b60c77c3ec15 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts @@ -19,6 +19,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // We need to use a dataset for the tests to run const data = [ { + '@timestamp': new Date().toISOString(), resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' }, result: { evaluation: chance.integer() % 2 === 0 ? 'passed' : 'failed' }, rule: { @@ -40,7 +41,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { cluster_id: 'Upper case cluster id', }, { - '@timestamp': '2023-09-10T14:01:00.000Z', + '@timestamp': new Date(Date.now() - 60 * 60 * 1000).toISOString(), resource: { id: chance.guid(), name: `Pod`, sub_type: 'Upper case sub type' }, result: { evaluation: chance.integer() % 2 === 0 ? 'passed' : 'failed' }, rule: { @@ -62,7 +63,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { cluster_id: 'Another Upper case cluster id', }, { - '@timestamp': '2023-09-10T14:02:00.000Z', + '@timestamp': new Date(Date.now() - 60 * 60 * 1000).toISOString(), resource: { id: chance.guid(), name: `process`, sub_type: 'another lower case type' }, result: { evaluation: 'passed' }, rule: { @@ -84,7 +85,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { cluster_id: 'lower case cluster id', }, { - '@timestamp': '2023-09-10T14:03:00.000Z', + '@timestamp': new Date(Date.now() - 60 * 60 * 1000).toISOString(), resource: { id: chance.guid(), name: `process`, sub_type: 'Upper case type again' }, result: { evaluation: 'failed' }, rule: { diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts new file mode 100644 index 0000000000000..173630e56837e --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts @@ -0,0 +1,309 @@ +/* + * Copyright 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 Chance from 'chance'; +import { asyncForEach } from '@kbn/std'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const queryBar = getService('queryBar'); + const filterBar = getService('filterBar'); + const pageObjects = getPageObjects(['common', 'findings', 'header']); + const chance = new Chance(); + + // We need to use a dataset for the tests to run + // We intentionally make some fields start with a capital letter to test that the query bar is case-insensitive/case-sensitive + const data = [ + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' }, + result: { evaluation: chance.integer() % 2 === 0 ? 'passed' : 'failed' }, + orchestrator: { + cluster: { + id: '1', + name: 'Cluster 1', + }, + }, + rule: { + name: 'Upper case rule name', + section: 'Upper case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + name: 'CIS Kubernetes V1.23', + version: 'v1.0.0', + }, + type: 'process', + }, + cluster_id: 'Upper case cluster id', + }, + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `Pod`, sub_type: 'Upper case sub type' }, + result: { evaluation: chance.integer() % 2 === 0 ? 'passed' : 'failed' }, + cloud: { + account: { + id: '1', + name: 'Account 1', + }, + }, + rule: { + name: 'lower case rule name', + section: 'Another upper case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + name: 'CIS Kubernetes V1.23', + version: 'v1.0.0', + }, + type: 'process', + }, + cluster_id: 'Another Upper case cluster id', + }, + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `process`, sub_type: 'another lower case type' }, + result: { evaluation: 'passed' }, + cloud: { + account: { + id: '1', + name: 'Account 1', + }, + }, + rule: { + name: 'Another upper case rule name', + section: 'lower case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + name: 'CIS Kubernetes V1.23', + version: 'v1.0.0', + }, + type: 'process', + }, + cluster_id: 'lower case cluster id', + }, + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `process`, sub_type: 'Upper case type again' }, + result: { evaluation: 'failed' }, + cloud: { + account: { + id: '2', + name: 'Account 2', + }, + }, + rule: { + name: 'some lower case rule name', + section: 'another lower case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + name: 'CIS Kubernetes V1.23', + version: 'v1.0.0', + }, + type: 'process', + }, + cluster_id: 'another lower case cluster id', + }, + ]; + + const ruleName1 = data[0].rule.name; + + describe('Findings Page - Grouping', function () { + this.tags(['cloud_security_posture_findings_grouping']); + let findings: typeof pageObjects.findings; + // let groupSelector: ReturnType; + + before(async () => { + findings = pageObjects.findings; + + // Before we start any test we must wait for cloud_security_posture plugin to complete its initialization + await findings.waitForPluginInitialized(); + + // Prepare mocked findings + await findings.index.remove(); + await findings.index.add(data); + + await findings.navigateToLatestFindingsPage(); + await pageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('None'); + await findings.index.remove(); + }); + + describe('Default Grouping', async () => { + it('groups findings by resource and sort case sensitive asc', async () => { + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('Resource'); + + const grouping = await findings.findingsGrouping(); + + const resourceOrder = ['Pod', 'kubelet', 'process']; + + await asyncForEach(resourceOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('3 groups'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); + }); + it('groups findings by rule name and sort case sensitive asc', async () => { + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('Rule name'); + + const grouping = await findings.findingsGrouping(); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('4 groups'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); + + const ruleNameOrder = [ + 'Another upper case rule name', + 'Upper case rule name', + 'lower case rule name', + 'some lower case rule name', + ]; + + await asyncForEach(ruleNameOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + }); + it('groups findings by cloud account and sort case sensitive asc', async () => { + const groupSelector = await findings.groupSelector(); + + await groupSelector.setValue('Cloud account'); + + const grouping = await findings.findingsGrouping(); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('3 groups'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); + + const cloudNameOrder = ['Account 1', 'Account 2', '—']; + + await asyncForEach(cloudNameOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + }); + it('groups findings by Kubernetes cluster and sort case sensitive asc', async () => { + const groupSelector = await findings.groupSelector(); + await groupSelector.setValue('Kubernetes cluster'); + + const grouping = await findings.findingsGrouping(); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('2 groups'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); + + const cloudNameOrder = ['Cluster 1', '—']; + + await asyncForEach(cloudNameOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + }); + }); + describe('SearchBar', () => { + it('add filter', async () => { + const groupSelector = await findings.groupSelector(); + await groupSelector.setValue('Resource'); + + // Filter bar uses the field's customLabel in the DataView + await filterBar.addFilter({ field: 'Rule Name', operation: 'is', value: ruleName1 }); + expect(await filterBar.hasFilter('rule.name', ruleName1)).to.be(true); + + const grouping = await findings.findingsGrouping(); + + const resourceOrder = ['kubelet']; + + await asyncForEach(resourceOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('1 group'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('1 finding'); + }); + + it('remove filter', async () => { + await filterBar.removeFilter('rule.name'); + + expect(await filterBar.hasFilter('rule.name', ruleName1)).to.be(false); + + const grouping = await findings.findingsGrouping(); + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('3 groups'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); + }); + + it('set search query', async () => { + await queryBar.setQuery(ruleName1); + await queryBar.submitQuery(); + + const grouping = await findings.findingsGrouping(); + + const resourceOrder = ['kubelet']; + + await asyncForEach(resourceOrder, async (resourceName, index) => { + const groupName = await grouping.getRowAtIndex(index); + expect(await groupName.getVisibleText()).to.be(resourceName); + }); + + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be('1 group'); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('1 finding'); + + await queryBar.setQuery(''); + await queryBar.submitQuery(); + + expect(await grouping.getGroupCount()).to.be('3 groups'); + expect(await grouping.getUnitCount()).to.be('4 findings'); + }); + }); + + describe('Group table', async () => { + it('shows findings table when expanding', async () => { + const grouping = await findings.findingsGrouping(); + const firstRow = await grouping.getRowAtIndex(0); + await (await firstRow.findByCssSelector('button')).click(); + const latestFindingsTable = findings.createDataTableObject('latest_findings_table'); + expect(await latestFindingsTable.getRowsCount()).to.be(1); + expect(await latestFindingsTable.hasColumnValue('rule.name', 'lower case rule name')).to.be( + true + ); + }); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_old_data.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_old_data.ts index a8cda10482e2e..9586387c028ea 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings_old_data.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_old_data.ts @@ -10,7 +10,7 @@ import Chance from 'chance'; import type { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function ({ getPageObjects, getService }: FtrProviderContext) { +export default function ({ getPageObjects }: FtrProviderContext) { const pageObjects = getPageObjects(['common', 'findings', 'header']); const chance = new Chance(); const hoursToMillisecond = (hours: number) => hours * 60 * 60 * 1000; @@ -55,7 +55,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, ]; - describe.skip('Old Data', function () { + describe('Old Data', function () { this.tags(['cloud_security_posture_findings']); let findings: typeof pageObjects.findings; @@ -77,7 +77,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await findings.index.add(dataOldKspm); await findings.navigateToLatestFindingsPage(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await findings.isLatestFindingsTableThere()).to.be(false); }); it('returns no Findings CSPM', async () => { @@ -86,7 +86,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await findings.index.add(dataOldCspm); await findings.navigateToLatestFindingsPage(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await findings.isLatestFindingsTableThere()).to.be(false); }); }); diff --git a/x-pack/test/cloud_security_posture_functional/pages/index.ts b/x-pack/test/cloud_security_posture_functional/pages/index.ts index f1bb7f8fb7875..9da8cbbeeed54 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/index.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/index.ts @@ -12,6 +12,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('Cloud Security Posture', function () { loadTestFile(require.resolve('./findings_onboarding')); loadTestFile(require.resolve('./findings')); + loadTestFile(require.resolve('./findings_grouping')); loadTestFile(require.resolve('./findings_alerts')); loadTestFile(require.resolve('./compliance_dashboard')); loadTestFile(require.resolve('./vulnerability_dashboard')); diff --git a/x-pack/test/dataset_quality_api_integration/common/config.ts b/x-pack/test/dataset_quality_api_integration/common/config.ts index 7a0c7c08fb2f0..e10ad4273fe76 100644 --- a/x-pack/test/dataset_quality_api_integration/common/config.ts +++ b/x-pack/test/dataset_quality_api_integration/common/config.ts @@ -13,6 +13,7 @@ import { createDatasetQualityUsers } from '@kbn/dataset-quality-plugin/server/te import { FtrConfigProviderContext } from '@kbn/test'; import supertest from 'supertest'; import { format, UrlObject } from 'url'; +import { createLogger, LogLevel, LogsSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { FtrProviderContext, InheritedFtrProviderContext, @@ -64,6 +65,9 @@ export interface CreateTest { services: InheritedServices & { datasetQualityFtrConfig: () => DatasetQualityFtrConfig; registry: ({ getService }: FtrProviderContext) => ReturnType; + logSynthtraceEsClient: ( + context: InheritedFtrProviderContext + ) => Promise; datasetQualityApiClient: (context: InheritedFtrProviderContext) => DatasetQualityApiClient; }; junit: { reportName: string }; @@ -95,6 +99,12 @@ export function createTestConfig( ...services, datasetQualityFtrConfig: () => config, registry: RegistryProvider, + logSynthtraceEsClient: (context: InheritedFtrProviderContext) => + new LogsSynthtraceEsClient({ + client: context.getService('es'), + logger: createLogger(LogLevel.info), + refreshAfterIndex: true, + }), datasetQualityApiClient: async (_: InheritedFtrProviderContext) => { const { username, password } = servers.kibana; const esUrl = format(esServer); diff --git a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts index d28ddb5f04962..79818e970a2ab 100644 --- a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts +++ b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts @@ -10,10 +10,7 @@ import supertest from 'supertest'; import request from 'superagent'; import type { APIEndpoint } from '@kbn/dataset-quality-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; -import { - APIClientRequestParamsOf, - APIReturnType, -} from '@kbn/dataset-quality-plugin/public/services/rest/create_call_dataset_quality_api'; +import type { APIClientRequestParamsOf, APIReturnType } from '@kbn/dataset-quality-plugin/common'; export function createDatasetQualityApiClient(st: supertest.SuperTest) { return async ( diff --git a/x-pack/test/dataset_quality_api_integration/tests/data_streams.spec.ts b/x-pack/test/dataset_quality_api_integration/tests/data_streams.spec.ts index cd236c109c667..6d11326bf213e 100644 --- a/x-pack/test/dataset_quality_api_integration/tests/data_streams.spec.ts +++ b/x-pack/test/dataset_quality_api_integration/tests/data_streams.spec.ts @@ -5,15 +5,19 @@ * 2.0. */ +import { log, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { DatasetQualityApiClientKey } from '../common/config'; import { DatasetQualityApiError } from '../common/dataset_quality_api_supertest'; import { FtrProviderContext } from '../common/ftr_provider_context'; import { expectToReject } from '../utils'; +import { cleanLogIndexTemplate, addIntegrationToLogIndexTemplate } from './es_utils'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); + const synthtrace = getService('logSynthtraceEsClient'); const datasetQualityApiClient = getService('datasetQualityApiClient'); + const es = getService('es'); async function callApiAs(user: DatasetQualityApiClientKey) { return await datasetQualityApiClient[user]({ @@ -21,7 +25,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { params: { query: { type: 'logs', - sortOrder: 'asc', }, }, }); @@ -40,10 +43,67 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('when required privileges are set', () => { - it('returns true when user has logMonitoring privileges', async () => { - const privileges = await callApiAs('datasetQualityLogsUser'); + describe('and uncategorized datastreams', () => { + const integration = 'my-custom-integration'; - expect(privileges.body.items.length).to.be(0); + before(async () => { + await addIntegrationToLogIndexTemplate({ esClient: es, name: integration }); + + await synthtrace.index([ + timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z') + .interval('1m') + .rate(1) + .generator((timestamp) => + log.create().message('This is a log message').timestamp(timestamp).defaults({ + 'log.file.path': '/my-service.log', + }) + ), + ]); + }); + + it('returns stats correctly', async () => { + const stats = await callApiAs('datasetQualityLogsUser'); + + expect(stats.body.dataStreamsStats.length).to.be(1); + expect(stats.body.dataStreamsStats[0].integration).to.be(integration); + expect(stats.body.dataStreamsStats[0].size).not.empty(); + expect(stats.body.dataStreamsStats[0].size_bytes).greaterThan(0); + expect(stats.body.dataStreamsStats[0].last_activity).greaterThan(0); + }); + + after(async () => { + await synthtrace.clean(); + await cleanLogIndexTemplate({ esClient: es }); + }); + }); + + describe('and categorized datastreams', () => { + before(async () => { + await synthtrace.index([ + timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z') + .interval('1m') + .rate(1) + .generator((timestamp) => + log.create().message('This is a log message').timestamp(timestamp).defaults({ + 'log.file.path': '/my-service.log', + }) + ), + ]); + }); + + it('returns stats correctly', async () => { + const stats = await callApiAs('datasetQualityLogsUser'); + + expect(stats.body.dataStreamsStats.length).to.be(1); + expect(stats.body.dataStreamsStats[0].integration).not.ok(); + expect(stats.body.dataStreamsStats[0].size).not.empty(); + expect(stats.body.dataStreamsStats[0].size_bytes).greaterThan(0); + expect(stats.body.dataStreamsStats[0].last_activity).greaterThan(0); + }); + + after(async () => { + await synthtrace.clean(); + }); }); }); }); diff --git a/x-pack/test/dataset_quality_api_integration/tests/es_utils.ts b/x-pack/test/dataset_quality_api_integration/tests/es_utils.ts new file mode 100644 index 0000000000000..607522089952f --- /dev/null +++ b/x-pack/test/dataset_quality_api_integration/tests/es_utils.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 { Client } from '@elastic/elasticsearch'; + +export async function addIntegrationToLogIndexTemplate({ + esClient, + name, + managedBy = 'fleet', +}: { + esClient: Client; + name: string; + managedBy?: string; +}) { + const { index_templates: indexTemplates } = await esClient.indices.getIndexTemplate({ + name: 'logs', + }); + + await esClient.indices.putIndexTemplate({ + name: 'logs', + body: { + ...indexTemplates[0].index_template, + _meta: { + ...indexTemplates[0].index_template._meta, + package: { + name, + }, + managed_by: managedBy, + }, + }, + }); +} + +export async function cleanLogIndexTemplate({ esClient }: { esClient: Client }) { + const { index_templates: indexTemplates } = await esClient.indices.getIndexTemplate({ + name: 'logs', + }); + + await esClient.indices.putIndexTemplate({ + name: 'logs', + body: { + ...indexTemplates[0].index_template, + _meta: { + ...indexTemplates[0].index_template._meta, + package: undefined, + managed_by: undefined, + }, + }, + }); +} diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/index.ts b/x-pack/test/detection_engine_api_integration/basic/tests/index.ts index 315a06043684f..5b3449707d38f 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile }: FtrProviderContext): void => { describe('detection engine api basic license', function () { - loadTestFile(require.resolve('./create_rules')); loadTestFile(require.resolve('./create_rules_bulk')); loadTestFile(require.resolve('./delete_rules')); loadTestFile(require.resolve('./delete_rules_bulk')); @@ -22,8 +21,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./update_rules_bulk')); loadTestFile(require.resolve('./patch_rules_bulk')); loadTestFile(require.resolve('./patch_rules')); - loadTestFile(require.resolve('./query_signals')); - loadTestFile(require.resolve('./open_close_signals')); loadTestFile(require.resolve('./import_timelines')); loadTestFile(require.resolve('./coverage_overview')); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts index 6404da38cdde7..982557130717e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts @@ -447,7 +447,7 @@ export default ({ getService }: FtrProviderContext): void => { .expect(400); expect(body.message).to.eql( - '[request body]: 0.investigation_fields: Expected object, received array, 0.type: Invalid literal value, expected "eql", 0.language: Invalid literal value, expected "eql", 0.investigation_fields: Expected object, received array, 0.investigation_fields: Expected object, received array, and 22 more' + '[request body]: 0.investigation_fields: Expected object, received array' ); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts index 8943a4b67c99b..aee267db951d5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_export_rules.ts @@ -26,8 +26,8 @@ import { deleteAllRules, deleteAllAlerts, getSimpleRule, + deleteAllExceptions, } from '../../utils'; -import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; // This test was meant to be more full flow, ensuring that 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 c92591e8f3f74..6517c46bddcaf 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 @@ -38,8 +38,8 @@ import { createRule, getRule, getRuleSOById, + deleteAllExceptions, } from '../../utils'; -import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; const getImportRuleBuffer = (connectorId: string) => { @@ -411,11 +411,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.errors[0]).to.eql({ rule_id: '(unknown id)', - error: { - message: - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', - status_code: 400, - }, + error: { status_code: 400, message: 'threshold: Required' }, }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index 2529e794089a9..d14d82edbba51 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -23,7 +23,6 @@ import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/com import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; import { WebhookAuthType } from '@kbn/stack-connectors-plugin/common/webhook/constants'; -import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { binaryToString, createLegacyRuleAction, @@ -44,6 +43,7 @@ import { createRuleThroughAlertingEndpoint, getRuleSavedObjectWithLegacyInvestigationFields, getRuleSavedObjectWithLegacyInvestigationFieldsEmptyArray, + deleteAllExceptions, } from '../../utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index d88ed8a898f90..afb0205ce458f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -566,8 +566,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).to.eql({ error: 'Bad Request', - message: - '[request body]: type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', + message: '[request body]: threshold: Required', statusCode: 400, }); }); @@ -957,7 +956,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).to.eql( - '[request body]: investigation_fields: Expected object, received array, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", investigation_fields: Expected object, received array, investigation_fields: Expected object, received array, and 22 more' + '[request body]: investigation_fields: Expected object, received array' ); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index a3defbb6d1c82..569069cee3062 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -854,7 +854,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).to.eql( - '[request body]: 0.investigation_fields: Expected object, received array, 0.type: Invalid literal value, expected "eql", 0.language: Invalid literal value, expected "eql", 0.investigation_fields: Expected object, received array, 0.investigation_fields: Expected object, received array, and 22 more' + '[request body]: 0.investigation_fields: Expected object, received array' ); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts index 002bf3ddda8a4..f3fb8671906c6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts @@ -27,8 +27,8 @@ import { getWebHookAction, removeServerGeneratedProperties, ruleToNdjson, + deleteAllExceptions, } from '../../utils'; -import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; const getImportRuleBuffer = (connectorId: string) => { diff --git a/x-pack/test/detection_engine_api_integration/utils/delete_all_exceptions.ts b/x-pack/test/detection_engine_api_integration/utils/delete_all_exceptions.ts new file mode 100644 index 0000000000000..aed98bc61561a --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/delete_all_exceptions.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// Should be deleted once all all the remaining tests in this folder get moved to the new /security_solution_api_integration folder + +import type SuperTest from 'supertest'; + +import type { ExceptionList, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; +import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; + +import { ToolingLog } from '@kbn/tooling-log'; +import { countDownTest } from './count_down_test'; + +/** + * Remove all exceptions from both the "single" and "agnostic" spaces. + * This will retry 50 times before giving up and hopefully still not interfere with other tests + * @param supertest The supertest handle + */ +export const deleteAllExceptions = async ( + supertest: SuperTest.SuperTest, + log: ToolingLog +): Promise => { + await deleteAllExceptionsByType(supertest, log, 'single'); + await deleteAllExceptionsByType(supertest, log, 'agnostic'); +}; + +/** + * Remove all exceptions by a given type such as "agnostic" or "single". + * This will retry 50 times before giving up and hopefully still not interfere with other tests + * @param supertest The supertest handle + */ +export const deleteAllExceptionsByType = async ( + supertest: SuperTest.SuperTest, + log: ToolingLog, + type: NamespaceType +): Promise => { + await countDownTest( + async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_URL}/_find?per_page=9999&namespace_type=${type}`) + .set('kbn-xsrf', 'true') + .send(); + const ids: string[] = body.data.map((exception: ExceptionList) => exception.id); + for await (const id of ids) { + await supertest + .delete(`${EXCEPTION_LIST_URL}?id=${id}&namespace_type=${type}`) + .set('kbn-xsrf', 'true') + .send(); + } + const { body: finalCheck } = await supertest + .get(`${EXCEPTION_LIST_URL}/_find?namespace_type=${type}`) + .set('kbn-xsrf', 'true') + .send(); + return { + passed: finalCheck.data.length === 0, + }; + }, + `deleteAllExceptions by type: "${type}"`, + log, + 50, + 1000 + ); +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/index.ts b/x-pack/test/detection_engine_api_integration/utils/index.ts index a1373f03a1817..1b18044d95449 100644 --- a/x-pack/test/detection_engine_api_integration/utils/index.ts +++ b/x-pack/test/detection_engine_api_integration/utils/index.ts @@ -44,7 +44,6 @@ export * from './get_rule_for_signal_testing_with_timestamp_override'; export * from './get_rule_with_web_hook_action'; export * from './get_rule_with_legacy_investigation_fields'; export * from './get_saved_query_rule_for_signal_testing'; -export * from './get_signal_status'; export * from './get_signals_by_id'; export * from './get_signals_by_ids'; export * from './get_signals_by_rule_ids'; @@ -87,3 +86,4 @@ export * from './prebuilt_rules/delete_all_prebuilt_rule_assets'; export * from './prebuilt_rules/install_mock_prebuilt_rules'; export * from './prebuilt_rules/install_prebuilt_rules_and_timelines'; export * from './get_legacy_action_so'; +export * from './delete_all_exceptions'; diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_dynamic_template_metric.ts b/x-pack/test/fleet_api_integration/apis/epm/install_dynamic_template_metric.ts index cc2bb4ebd8582..9bcf3fc673eab 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_dynamic_template_metric.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_dynamic_template_metric.ts @@ -24,14 +24,14 @@ export default function (providerContext: FtrProviderContext) { setupFleetAndAgents(providerContext); after(async () => { - await deletePackage('istio', '0.3.3'); + await deletePackage('no_tsdb_to_tsdb', '0.2.0'); }); it('should install with metric_type added as time_series_metric', async function () { - const templateName = 'metrics-istio.istiod_metrics@package'; + const templateName = 'metrics-no_tsdb_to_tsdb.test@package'; await supertest - .post(`/api/fleet/epm/packages/istio/0.3.3`) + .post(`/api/fleet/epm/packages/no_tsdb_to_tsdb/0.2.0`) .set('kbn-xsrf', 'xxxx') .send({ force: true }) .expect(200); @@ -46,7 +46,7 @@ export default function (providerContext: FtrProviderContext) { const template = resp.component_templates[0].component_template; const dynamicTemplates = template.template.mappings.dynamic_templates; - const mappingName = 'istio.istiod.metrics.*.counter'; + const mappingName = 'test.metrics.*.counter'; const counter = dynamicTemplates.find((tmpl: any) => Object.keys(tmpl)[0] === mappingName); expect(counter[mappingName].mapping.time_series_metric).to.eql('counter'); diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts b/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts index 7649237f0ab4d..5192b8a4e914b 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts @@ -16,10 +16,11 @@ export default function (providerContext: FtrProviderContext) { const pkgName = 'error_handling'; const goodPackageVersion = '0.1.0'; const badPackageVersion = '0.2.0'; + const goodUpgradePackageVersion = '0.3.0'; const kibanaServer = getService('kibanaServer'); - const installPackage = async (pkg: string, version: string) => { - await supertest + const installPackage = (pkg: string, version: string) => { + return supertest .post(`/api/fleet/epm/packages/${pkg}/${version}`) .set('kbn-xsrf', 'xxxx') .send({ force: true }); @@ -45,28 +46,46 @@ export default function (providerContext: FtrProviderContext) { afterEach(async () => { await kibanaServer.savedObjects.cleanStandardList(); await uninstallPackage(pkgName, goodPackageVersion); + await uninstallPackage(pkgName, goodUpgradePackageVersion); }); it('on a fresh install, it should uninstall a broken package during rollback', async function () { - await supertest - .post(`/api/fleet/epm/packages/${pkgName}/${badPackageVersion}`) - .set('kbn-xsrf', 'xxxx') - .expect(422); // the broken package contains a broken visualization triggering a 422 from Kibana + // the broken package contains a broken visualization triggering a 422 from Kibana + await installPackage(pkgName, badPackageVersion).expect(422); const pkgInfoResponse = await getPackageInfo(pkgName, badPackageVersion); expect(JSON.parse(pkgInfoResponse.text).item.status).to.be('not_installed'); + expect(pkgInfoResponse.body.item.savedObject).to.be(undefined); }); it('on an upgrade, it should fall back to the previous good version during rollback', async function () { await installPackage(pkgName, goodPackageVersion); - await supertest - .post(`/api/fleet/epm/packages/${pkgName}/${badPackageVersion}`) - .set('kbn-xsrf', 'xxxx') - .expect(422); // the broken package contains a broken visualization triggering a 422 from Kibana + // the broken package contains a broken visualization triggering a 422 from Kibana + await installPackage(pkgName, badPackageVersion).expect(422); const goodPkgInfoResponse = await getPackageInfo(pkgName, goodPackageVersion); expect(JSON.parse(goodPkgInfoResponse.text).item.status).to.be('installed'); expect(JSON.parse(goodPkgInfoResponse.text).item.version).to.be('0.1.0'); + const latestInstallFailedAttempts = + goodPkgInfoResponse.body.item.savedObject.attributes.latest_install_failed_attempts; + expect(latestInstallFailedAttempts).to.have.length(1); + expect(latestInstallFailedAttempts[0].target_version).to.be('0.2.0'); + expect(latestInstallFailedAttempts[0].error.message).to.contain( + 'Document "sample_visualization" belongs to a more recent version of Kibana [12.7.0]' + ); + }); + + it('on a succesfull upgrade, it should clear previous upgrade errors', async function () { + await installPackage(pkgName, goodPackageVersion); + await installPackage(pkgName, badPackageVersion).expect(422); + await installPackage(pkgName, goodUpgradePackageVersion).expect(200); + + const goodPkgInfoResponse = await getPackageInfo(pkgName, goodUpgradePackageVersion); + expect(JSON.parse(goodPkgInfoResponse.text).item.status).to.be('installed'); + expect(JSON.parse(goodPkgInfoResponse.text).item.version).to.be('0.3.0'); + const latestInstallFailedAttempts = + goodPkgInfoResponse.body.item.savedObject.attributes.latest_install_failed_attempts; + expect(latestInstallFailedAttempts).to.have.length(0); }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index aaf31e54798db..55d85aead4771 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -786,6 +786,7 @@ const expectAssetsInstalled = ({ install_status: 'installed', install_started_at: res.attributes.install_started_at, install_source: 'registry', + latest_install_failed_attempts: [], install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, verification_status: 'unknown', verification_key_id: null, diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index 75040c8400d04..eaea702cc6c8c 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -516,6 +516,7 @@ export default function (providerContext: FtrProviderContext) { install_started_at: res.attributes.install_started_at, install_source: 'registry', install_format_schema_version: FLEET_INSTALL_FORMAT_VERSION, + latest_install_failed_attempts: [], verification_status: 'unknown', verification_key_id: null, }); diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/docs/README.md b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/docs/README.md new file mode 100644 index 0000000000000..260499f4b0078 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/docs/README.md @@ -0,0 +1,3 @@ +This package should install without errors. + +Version 0.2.0 of this package should fail during installation. We need this good version to test rollback. \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/img/logo_overrides_64_color.svg b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/img/logo_overrides_64_color.svg new file mode 100644 index 0000000000000..b03007a76ffcc --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/img/logo_overrides_64_color.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/kibana/visualization/sample_visualization.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/kibana/visualization/sample_visualization.json new file mode 100644 index 0000000000000..01afe600853ef --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/kibana/visualization/sample_visualization.json @@ -0,0 +1,14 @@ +{ + "attributes": { + "description": "sample visualization", + "title": "sample vis title", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Log Level\",\"field\":\"log.level\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per day\"},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"stacked\",\"show\":\"true\",\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}]},\"title\":\"Log levels over time [Logs Kafka] ECS\",\"type\":\"histogram\"}" + }, + "id": "sample_visualization", + "type": "visualization", + "migrationVersion": { + "visualization": "7.7.0" + } +} diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/manifest.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/manifest.yml new file mode 100644 index 0000000000000..883e4234886e7 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.3.0/manifest.yml @@ -0,0 +1,22 @@ +format_version: 1.0.0 +name: error_handling +title: Error handling +description: tests error handling and rollback +version: 0.3.0 +categories: [] +release: beta +type: integration +license: basic +owner: + github: elastic/fleet + +requirement: + elasticsearch: + versions: '>7.7.0' + kibana: + versions: '>7.7.0' + +icons: + - src: '/img/logo_overrides_64_color.svg' + size: '16x16' + type: 'image/svg+xml' diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/no_tsdb_to_tsdb/0.2.0/data_stream/test/fields/fields.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/no_tsdb_to_tsdb/0.2.0/data_stream/test/fields/fields.yml index e1fd2db653efc..6cc90f2148d13 100644 --- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/no_tsdb_to_tsdb/0.2.0/data_stream/test/fields/fields.yml +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/no_tsdb_to_tsdb/0.2.0/data_stream/test/fields/fields.yml @@ -20,3 +20,10 @@ - name: 'some_metric_field' type: integer metric_type: gauge +- name: test.metrics.*.counter + type: object + object_type: double + object_type_mapping_type: "*" + metric_type: counter + description: > + Istiod counter metric diff --git a/x-pack/test/fleet_api_integration/apis/outputs/crud.ts b/x-pack/test/fleet_api_integration/apis/outputs/crud.ts index 5f6558df992ca..bd44a6be9427e 100644 --- a/x-pack/test/fleet_api_integration/apis/outputs/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/outputs/crud.ts @@ -1132,6 +1132,23 @@ export default function (providerContext: FtrProviderContext) { // @ts-ignore _source unknown type expect(secret._source.value).to.equal('pass'); }); + + it('should create service_token secret correctly', async function () { + const res = await supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Remote Elasticsearch With Service Token Secret', + type: 'remote_elasticsearch', + hosts: ['https://remote-es:9200'], + secrets: { service_token: 'token' }, + }); + + const secretId = res.body.item.secrets.service_token.id; + const secret = await getSecretById(secretId); + // @ts-ignore _source unknown type + expect(secret._source.value).to.equal('token'); + }); }); describe('DELETE /outputs/{outputId}', () => { diff --git a/x-pack/test/functional/apps/aiops/change_point_detection.ts b/x-pack/test/functional/apps/aiops/change_point_detection.ts index f643de514c0cb..e7ca426593342 100644 --- a/x-pack/test/functional/apps/aiops/change_point_detection.ts +++ b/x-pack/test/functional/apps/aiops/change_point_detection.ts @@ -16,23 +16,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // aiops lives in the ML UI so we need some related services. const ml = getService('ml'); - describe('change point detection', async function () { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/172203 + describe.skip('change point detection', async function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it(`loads the change point detection page`, async () => { // Start navigation from the base of the ML app. await ml.navigation.navigateToMl(); await elasticChart.setNewChartUiDebugFlag(true); - await aiops.changePointDetectionPage.navigateToIndexPatternSelection(); + await aiops.changePointDetectionPage.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForChangePointDetection('ft_ecommerce'); await aiops.changePointDetectionPage.assertChangePointDetectionPageExists(); }); diff --git a/x-pack/test/functional/apps/aiops/log_pattern_analysis.ts b/x-pack/test/functional/apps/aiops/log_pattern_analysis.ts index 921d02c8c0c67..6ea8a1d4abc36 100644 --- a/x-pack/test/functional/apps/aiops/log_pattern_analysis.ts +++ b/x-pack/test/functional/apps/aiops/log_pattern_analysis.ts @@ -36,20 +36,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await ml.testResources.createIndexPatternIfNeeded('logstash-*', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('logstash-*', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('logstash-*'); + await ml.testResources.deleteDataViewByTitle('logstash-*'); }); it(`loads the log pattern analysis page and filters in patterns in discover`, async () => { // Start navigation from the base of the ML app. await ml.navigation.navigateToMl(); await elasticChart.setNewChartUiDebugFlag(true); - await aiops.logPatternAnalysisPage.navigateToIndexPatternSelection(); + await aiops.logPatternAnalysisPage.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForLogPatternAnalysisDetection('logstash-*'); await aiops.logPatternAnalysisPage.assertLogPatternAnalysisPageExists(); @@ -76,7 +76,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // Start navigation from the base of the ML app. await ml.navigation.navigateToMl(); await elasticChart.setNewChartUiDebugFlag(true); - await aiops.logPatternAnalysisPage.navigateToIndexPatternSelection(); + await aiops.logPatternAnalysisPage.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForLogPatternAnalysisDetection('logstash-*'); await aiops.logPatternAnalysisPage.assertLogPatternAnalysisPageExists(); diff --git a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts index 9845cf0a5180f..403bbe6382f6c 100644 --- a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts +++ b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts @@ -36,13 +36,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await ml.testResources.createIndexPatternIfNeeded('logstash-*', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('logstash-*', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle('logstash-*'); + await ml.testResources.deleteDataViewByTitle('logstash-*'); }); it(`loads the log pattern analysis flyout and shows patterns in discover`, async () => { diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis.ts index 8e33b4b1c8e4a..f613349078b48 100644 --- a/x-pack/test/functional/apps/aiops/log_rate_analysis.ts +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis.ts @@ -15,6 +15,7 @@ import { logRateAnalysisTestData } from './log_rate_analysis_test_data'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'console', 'header', 'home', 'security']); + const browser = getService('browser'); const elasticChart = getService('elasticChart'); const aiops = getService('aiops'); @@ -28,7 +29,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await aiops.logRateAnalysisPage.navigateToIndexPatternSelection(); + await aiops.logRateAnalysisPage.navigateToDataViewSelection(); await ml.testExecution.logTestStep(`${testData.suiteTitle} loads the log rate analysis page`); await ml.jobSourceSelection.selectSourceForLogRateAnalysis(testData.sourceIndexOrSavedSearch); @@ -147,7 +148,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await aiops.logRateAnalysisPage.clickRerunAnalysisButton(true); } - await aiops.logRateAnalysisPage.assertAnalysisComplete(testData.analysisType); + // Wait for the analysis to finish + await aiops.logRateAnalysisPage.assertAnalysisComplete( + testData.analysisType, + testData.dataGenerator + ); + + // At this stage the baseline and deviation brush position should be stored in + // the url state and a full browser refresh should restore the analysis. + await browser.refresh(); + await aiops.logRateAnalysisPage.assertAnalysisComplete( + testData.analysisType, + testData.dataGenerator + ); // The group switch should be disabled by default await aiops.logRateAnalysisPage.assertLogRateAnalysisResultsGroupSwitchExists(false); @@ -160,8 +173,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const analysisGroupsTable = await aiops.logRateAnalysisResultsGroupsTable.parseAnalysisTable(); - expect(orderBy(analysisGroupsTable, 'group')).to.be.eql( - orderBy(testData.expected.analysisGroupsTable, 'group') + + const actualAnalysisGroupsTable = orderBy(analysisGroupsTable, 'group'); + const expectedAnalysisGroupsTable = orderBy(testData.expected.analysisGroupsTable, 'group'); + + expect(actualAnalysisGroupsTable).to.be.eql( + expectedAnalysisGroupsTable, + `Expected analysis groups table to be ${JSON.stringify( + expectedAnalysisGroupsTable + )}, got ${JSON.stringify(actualAnalysisGroupsTable)}` ); await ml.testExecution.logTestStep('expand table row'); @@ -170,8 +190,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { if (!isTestDataExpectedWithSampleProbability(testData.expected)) { const analysisTable = await aiops.logRateAnalysisResultsTable.parseAnalysisTable(); - expect(orderBy(analysisTable, ['fieldName', 'fieldValue'])).to.be.eql( - orderBy(testData.expected.analysisTable, ['fieldName', 'fieldValue']) + + const actualAnalysisTable = orderBy(analysisTable, ['fieldName', 'fieldValue']); + const expectedAnalysisTable = orderBy(testData.expected.analysisTable, [ + 'fieldName', + 'fieldValue', + ]); + + expect(actualAnalysisTable).to.be.eql( + expectedAnalysisTable, + `Expected analysis table results to be ${JSON.stringify( + expectedAnalysisTable + )}, got ${JSON.stringify(actualAnalysisTable)}` ); } @@ -199,8 +229,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { if (!isTestDataExpectedWithSampleProbability(testData.expected)) { const filteredAnalysisGroupsTable = await aiops.logRateAnalysisResultsGroupsTable.parseAnalysisTable(); - expect(orderBy(filteredAnalysisGroupsTable, 'group')).to.be.eql( - orderBy(testData.expected.filteredAnalysisGroupsTable, 'group') + + const actualFilteredAnalysisGroupsTable = orderBy(filteredAnalysisGroupsTable, 'group'); + const expectedFilteredAnalysisGroupsTable = orderBy( + testData.expected.filteredAnalysisGroupsTable, + 'group' + ); + + expect(actualFilteredAnalysisGroupsTable).to.be.eql( + expectedFilteredAnalysisGroupsTable, + `Expected filtered analysis groups table to be ${JSON.stringify( + expectedFilteredAnalysisGroupsTable + )}, got ${JSON.stringify(actualFilteredAnalysisGroupsTable)}` ); } } @@ -234,7 +274,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { before(async () => { await aiops.logRateAnalysisDataGenerator.generateData(testData.dataGenerator); - await ml.testResources.createIndexPatternIfNeeded( + await ml.testResources.createDataViewIfNeeded( testData.sourceIndexOrSavedSearch, '@timestamp' ); @@ -260,7 +300,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { after(async () => { await elasticChart.setNewChartUiDebugFlag(false); if (testData.dataGenerator !== 'kibana_sample_data_logs') { - await ml.testResources.deleteIndexPatternByTitle(testData.sourceIndexOrSavedSearch); + await ml.testResources.deleteDataViewByTitle(testData.sourceIndexOrSavedSearch); } await aiops.logRateAnalysisDataGenerator.removeGeneratedData(testData.dataGenerator); }); diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table.ts new file mode 100644 index 0000000000000..6780cab5cb209 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table.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. + */ + +export const analysisGroupsTable = [ + { + group: 'response_code: 500url: home.php', + docCount: '792', + }, + { + group: 'url: login.phpresponse_code: 500', + docCount: '790', + }, + { + docCount: '636', + group: 'user: Peterurl: home.php', + }, + { + docCount: '632', + group: 'user: Peterurl: login.php', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield.ts new file mode 100644 index 0000000000000..2f20ab7900386 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield.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. + */ + +export const analysisGroupsTableTextfield = [ + { + group: 'message: an unexpected error occuredurl: home.phpresponse_code: 500', + docCount: '634', + }, + { + group: 'message: an unexpected error occuredurl: login.phpresponse_code: 500', + docCount: '632', + }, + { + docCount: '636', + group: 'user: Peterurl: home.php', + }, + { + docCount: '632', + group: 'user: Peterurl: login.php', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield_zerodocsfallback.ts new file mode 100644 index 0000000000000..4c9308f6c0ae2 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_textfield_zerodocsfallback.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const analysisGroupsTableTextfieldZerodocsfallback = [ + { + group: + 'message: an unexpected error occuredurl: home.phpuser: Maryresponse_code: 500version: v1.0.0', + docCount: '29', + }, + { + group: + 'message: an unexpected error occuredurl: home.phpuser: Paulresponse_code: 500version: v1.0.0', + docCount: '29', + }, + { + group: + 'message: an unexpected error occuredurl: login.phpuser: Paulresponse_code: 500version: v1.0.0', + docCount: '29', + }, + { + group: + 'url: home.phpuser: Paulresponse_code: 500message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterresponse_code: 200url: home.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterresponse_code: 200url: login.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterresponse_code: 404url: home.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterresponse_code: 404url: login.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterurl: user.phpresponse_code: 200message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, + { + group: + 'user: Peterurl: user.phpresponse_code: 404message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '30', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_zerodocsfallback.ts new file mode 100644 index 0000000000000..cd749df4f2921 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_groups_table_zerodocsfallback.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const analysisGroupsTableZerodocsfallback = [ + { group: 'response_code: 500url: home.phpuser: Maryversion: v1.0.0', docCount: '47' }, + { group: 'response_code: 500url: home.phpuser: Paulversion: v1.0.0', docCount: '59' }, + { group: 'response_code: 500url: login.phpuser: Maryversion: v1.0.0', docCount: '35' }, + { group: 'response_code: 500url: login.phpuser: Paulversion: v1.0.0', docCount: '39' }, + { group: 'user: Peterurl: home.phpresponse_code: 200version: v1.0.0', docCount: '30' }, + { group: 'user: Peterurl: home.phpresponse_code: 404version: v1.0.0', docCount: '30' }, + { group: 'user: Peterurl: login.phpresponse_code: 200version: v1.0.0', docCount: '30' }, + { group: 'user: Peterurl: login.phpresponse_code: 404version: v1.0.0', docCount: '30' }, + { group: 'user: Peterurl: user.phpresponse_code: 200version: v1.0.0', docCount: '30' }, + { group: 'user: Peterurl: user.phpresponse_code: 404version: v1.0.0', docCount: '30' }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table.ts new file mode 100644 index 0000000000000..797a9b0911489 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table.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. + */ + +export const analysisTable = [ + { + fieldName: 'url', + fieldValue: 'home.php', + impact: 'Low', + logRate: 'Chart type:bar chart', + pValue: '0.00974', + }, + { + fieldName: 'user', + fieldValue: 'Peter', + impact: 'High', + logRate: 'Chart type:bar chart', + pValue: '2.63e-21', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield.ts new file mode 100644 index 0000000000000..be77ef870d536 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const analysisTableTextfield = [ + { + fieldName: 'message', + fieldValue: 'an unexpected error occured', + logRate: 'Chart type:bar chart', + pValue: '0.00000100', + impact: 'Medium', + }, + { + fieldName: 'response_code', + fieldValue: '500', + logRate: 'Chart type:bar chart', + pValue: '3.61e-12', + impact: 'High', + }, + { + fieldName: 'url', + fieldValue: 'home.php', + impact: 'Low', + logRate: 'Chart type:bar chart', + pValue: '0.00974', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield_zerodocsfallback.ts new file mode 100644 index 0000000000000..75c624a67321c --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_textfield_zerodocsfallback.ts @@ -0,0 +1,44 @@ +/* + * Copyright 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 analysisTableTextfieldZerodocsfallback = [ + { + fieldName: 'message', + fieldValue: 'Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'response_code', + fieldValue: '500', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'url', + fieldValue: 'home.php', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'user', + fieldValue: 'Paul', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'version', + fieldValue: 'v1.0.0', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_zerodocsfallback.ts new file mode 100644 index 0000000000000..3d4f806db8f49 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/analysis_table_zerodocsfallback.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const analysisTableZerodocsfallback = [ + { + fieldName: 'response_code', + fieldValue: '500', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'url', + fieldValue: 'home.php', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'user', + fieldValue: 'Paul', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, + { + fieldName: 'version', + fieldValue: 'v1.0.0', + logRate: 'Chart type:bar chart', + pValue: '1.00', + impact: '', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table.ts new file mode 100644 index 0000000000000..97b1e2ce7169b --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table.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 filteredAnalysisGroupsTable = [ + { group: '* url: home.phpresponse_code: 500', docCount: '792' }, + { group: '* url: login.phpresponse_code: 500', docCount: '790' }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield.ts new file mode 100644 index 0000000000000..41c5efe2cb211 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const filteredAnalysisGroupsTableTextfield = [ + { + group: '* url: home.phpmessage: an unexpected error occuredresponse_code: 500', + docCount: '634', + }, + { + group: '* url: login.phpmessage: an unexpected error occuredresponse_code: 500', + docCount: '632', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield_zerodocsfallback.ts new file mode 100644 index 0000000000000..9ec9f0e19f89b --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_textfield_zerodocsfallback.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const filteredAnalysisGroupsTableTextfieldZerodocsfallback = [ + { + group: 'message: an unexpected error occuredurl: home.phpresponse_code: 500version: v1.0.0', + docCount: '58', + }, + { + group: 'message: an unexpected error occuredurl: login.phpresponse_code: 500version: v1.0.0', + docCount: '58', + }, + { + group: + 'response_code: 200url: home.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '46', + }, + { + group: + 'response_code: 200url: login.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '35', + }, + { + group: + 'response_code: 404url: home.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '63', + }, + { + group: + 'response_code: 404url: login.phpmessage: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '40', + }, + { + group: + 'url: home.phpresponse_code: 500message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '48', + }, + { + group: + 'url: user.phpresponse_code: 200message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '40', + }, + { + group: + 'url: user.phpresponse_code: 404message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '51', + }, + { + group: + 'url: user.phpresponse_code: 500message: Paul [11/19/2022, 8:00:34 AM] "GET /home.php HTTP/1.1" 200version: v1.0.0', + docCount: '41', + }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_zerodocsfallback.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_zerodocsfallback.ts new file mode 100644 index 0000000000000..eaeb04b9c0204 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/filtered_analysis_groups_table_zerodocsfallback.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. + */ + +export const filteredAnalysisGroupsTableZerodocsfallback = [ + { group: 'url: home.phpresponse_code: 200version: v1.0.0', docCount: '46' }, + { group: 'url: home.phpresponse_code: 404version: v1.0.0', docCount: '63' }, + { group: 'url: home.phpresponse_code: 500version: v1.0.0', docCount: '106' }, + { group: 'url: login.phpresponse_code: 200version: v1.0.0', docCount: '35' }, + { group: 'url: login.phpresponse_code: 404version: v1.0.0', docCount: '40' }, + { group: 'url: login.phpresponse_code: 500version: v1.0.0', docCount: '74' }, + { group: 'url: user.phpresponse_code: 200version: v1.0.0', docCount: '40' }, + { group: 'url: user.phpresponse_code: 404version: v1.0.0', docCount: '51' }, + { group: 'url: user.phpresponse_code: 500version: v1.0.0', docCount: '41' }, +]; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/artificial_log_data_view_test_data.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/artificial_log_data_view_test_data.ts new file mode 100644 index 0000000000000..6afbaab856424 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/artificial_log_data_view_test_data.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LogRateAnalysisType } from '@kbn/aiops-utils'; + +import type { TestData } from '../../types'; + +import type { LogRateAnalysisDataGenerator } from '../../../../services/aiops/log_rate_analysis_data_generator'; + +import { analysisGroupsTableTextfieldZerodocsfallback } from './__mocks__/analysis_groups_table_textfield_zerodocsfallback'; +import { analysisGroupsTableZerodocsfallback } from './__mocks__/analysis_groups_table_zerodocsfallback'; +import { analysisGroupsTableTextfield } from './__mocks__/analysis_groups_table_textfield'; +import { analysisGroupsTable } from './__mocks__/analysis_groups_table'; +import { filteredAnalysisGroupsTableTextfieldZerodocsfallback } from './__mocks__/filtered_analysis_groups_table_textfield_zerodocsfallback'; +import { filteredAnalysisGroupsTableZerodocsfallback } from './__mocks__/filtered_analysis_groups_table_zerodocsfallback'; +import { filteredAnalysisGroupsTableTextfield } from './__mocks__/filtered_analysis_groups_table_textfield'; +import { filteredAnalysisGroupsTable } from './__mocks__/filtered_analysis_groups_table'; +import { analysisTableTextfieldZerodocsfallback } from './__mocks__/analysis_table_textfield_zerodocsfallback'; +import { analysisTableZerodocsfallback } from './__mocks__/analysis_table_zerodocsfallback'; +import { analysisTableTextfield } from './__mocks__/analysis_table_textfield'; +import { analysisTable } from './__mocks__/analysis_table'; + +const REFERENCE_TS = 1669018354793; +const DAY_MS = 86400000; + +const DEVIATION_TS = REFERENCE_TS - DAY_MS * 2; +const BASELINE_TS = DEVIATION_TS - DAY_MS * 1; + +interface GetArtificialLogDataViewTestDataOptions { + analysisType: LogRateAnalysisType; + textField: boolean; + zeroDocsFallback: boolean; +} + +export const getArtificialLogDataViewTestData = ({ + analysisType, + textField, + zeroDocsFallback, +}: GetArtificialLogDataViewTestDataOptions): TestData => { + function getAnalysisGroupsTable() { + if (zeroDocsFallback) { + return textField + ? analysisGroupsTableTextfieldZerodocsfallback + : analysisGroupsTableZerodocsfallback; + } + return textField ? analysisGroupsTableTextfield : analysisGroupsTable; + } + + function getFilteredAnalysisGroupsTable() { + if (zeroDocsFallback) { + return textField + ? filteredAnalysisGroupsTableTextfieldZerodocsfallback + : filteredAnalysisGroupsTableZerodocsfallback; + } + + return textField ? filteredAnalysisGroupsTableTextfield : filteredAnalysisGroupsTable; + } + + function getAnalysisTable() { + if (zeroDocsFallback) { + return textField ? analysisTableTextfieldZerodocsfallback : analysisTableZerodocsfallback; + } + + return textField ? analysisTableTextfield : analysisTable; + } + + function getFieldSelectorPopover() { + if (zeroDocsFallback) { + return [...(textField ? ['message'] : []), 'response_code', 'url', 'user', 'version']; + } + return [...(textField ? ['message'] : []), 'response_code', 'url', 'user']; + } + + function getSuiteTitle() { + return `artificial logs with ${analysisType} and ${ + textField ? 'text field' : 'no text field' + } and ${zeroDocsFallback ? 'zero docs fallback' : 'no zero docs fallback'}`; + } + + function getDataGenerator(): LogRateAnalysisDataGenerator { + return `artificial_logs_with_${analysisType}${textField ? '_textfield' : ''}${ + zeroDocsFallback ? '_zerodocsfallback' : '' + }`; + } + + function getBrushBaselineTargetTimestamp() { + if (analysisType === 'dip' && zeroDocsFallback) { + return DEVIATION_TS; + } + + return zeroDocsFallback ? BASELINE_TS - DAY_MS / 2 : BASELINE_TS + DAY_MS / 2; + } + + function getBrushDeviationTargetTimestamp() { + if (analysisType === 'dip' && zeroDocsFallback) { + return DEVIATION_TS + DAY_MS * 1.5; + } + + return zeroDocsFallback ? DEVIATION_TS : DEVIATION_TS + DAY_MS / 2; + } + + return { + suiteTitle: getSuiteTitle(), + analysisType, + dataGenerator: getDataGenerator(), + isSavedSearch: false, + sourceIndexOrSavedSearch: getDataGenerator(), + brushBaselineTargetTimestamp: getBrushBaselineTargetTimestamp(), + brushDeviationTargetTimestamp: getBrushDeviationTargetTimestamp(), + brushIntervalFactor: zeroDocsFallback ? 1 : 10, + chartClickCoordinates: [-200, 30], + fieldSelectorSearch: 'user', + fieldSelectorApplyAvailable: true, + expected: { + totalDocCountFormatted: zeroDocsFallback ? '9,482' : '8,400', + analysisGroupsTable: getAnalysisGroupsTable(), + filteredAnalysisGroupsTable: getFilteredAnalysisGroupsTable(), + analysisTable: getAnalysisTable(), + fieldSelectorPopover: getFieldSelectorPopover(), + }, + }; +}; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data.ts new file mode 100644 index 0000000000000..be019193f2ef1 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-utils'; + +import type { TestData } from '../../types'; + +export const farequoteDataViewTestData: TestData = { + suiteTitle: 'farequote with spike', + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + dataGenerator: 'farequote_with_spike', + isSavedSearch: false, + sourceIndexOrSavedSearch: 'ft_farequote', + brushDeviationTargetTimestamp: 1455033600000, + brushIntervalFactor: 1, + chartClickCoordinates: [0, 0], + fieldSelectorSearch: 'airline', + fieldSelectorApplyAvailable: false, + expected: { + totalDocCountFormatted: '86,374', + sampleProbabilityFormatted: '0.5', + fieldSelectorPopover: ['airline', 'custom_field.keyword'], + }, +}; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data_with_query.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data_with_query.ts new file mode 100644 index 0000000000000..653889c1eb0ca --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/farequote_data_view_test_data_with_query.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-utils'; + +import type { TestData } from '../../types'; + +export const farequoteDataViewTestDataWithQuery: TestData = { + suiteTitle: 'farequote with spike', + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + dataGenerator: 'farequote_with_spike', + isSavedSearch: false, + sourceIndexOrSavedSearch: 'ft_farequote', + brushDeviationTargetTimestamp: 1455033600000, + brushIntervalFactor: 1, + chartClickCoordinates: [0, 0], + fieldSelectorSearch: 'airline', + fieldSelectorApplyAvailable: false, + query: 'NOT airline:("SWR" OR "ACA" OR "AWE" OR "BAW" OR "JAL" OR "JBU" OR "JZA" OR "KLM")', + expected: { + totalDocCountFormatted: '48,799', + analysisGroupsTable: [ + { + docCount: '297', + group: '* airline: AAL', + }, + { + docCount: '100', + group: '* custom_field.keyword: deviation* airline: UAL', + }, + ], + analysisTable: [ + { + fieldName: 'airline', + fieldValue: 'AAL', + logRate: 'Chart type:bar chart', + pValue: '1.18e-8', + impact: 'High', + }, + ], + fieldSelectorPopover: ['airline', 'custom_field.keyword'], + }, +}; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/kibana_logs_data_view_test_data.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/kibana_logs_data_view_test_data.ts new file mode 100644 index 0000000000000..2d85fe1e64210 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/kibana_logs_data_view_test_data.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-utils'; + +import type { TestData } from '../../types'; + +export const kibanaLogsDataViewTestData: TestData = { + suiteTitle: 'kibana sample data logs', + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + dataGenerator: 'kibana_sample_data_logs', + isSavedSearch: false, + sourceIndexOrSavedSearch: 'kibana_sample_data_logs', + brushIntervalFactor: 1, + chartClickCoordinates: [235, 0], + fieldSelectorSearch: 'referer', + fieldSelectorApplyAvailable: true, + action: { + type: 'LogPatternAnalysis', + tableRowId: '1064853178', + expected: { + queryBar: + 'clientip:30.156.16.164 AND host.keyword:elastic-elastic-elastic.org AND ip:30.156.16.163 AND response.keyword:404 AND machine.os.keyword:win xp AND geo.dest:IN AND geo.srcdest:US\\:IN', + totalDocCount: 100, + }, + }, + expected: { + totalDocCountFormatted: '14,074', + analysisGroupsTable: [ + { + group: + '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* referer: http://www.elastic-elastic-elastic.com/success/timothy-l-kopra* response.keyword: 404Showing 5 out of 8 items. 8 items unique to this group.', + docCount: '100', + }, + ], + filteredAnalysisGroupsTable: [ + { + group: + '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* response.keyword: 404* machine.os.keyword: win xpShowing 5 out of 7 items. 7 items unique to this group.', + docCount: '100', + }, + ], + analysisTable: [ + { + fieldName: 'clientip', + fieldValue: '30.156.16.164', + logRate: 'Chart type:bar chart', + pValue: '3.10e-13', + impact: 'High', + }, + { + fieldName: 'geo.dest', + fieldValue: 'IN', + logRate: 'Chart type:bar chart', + pValue: '0.000716', + impact: 'Medium', + }, + { + fieldName: 'geo.srcdest', + fieldValue: 'US:IN', + logRate: 'Chart type:bar chart', + pValue: '0.000716', + impact: 'Medium', + }, + { + fieldName: 'host.keyword', + fieldValue: 'elastic-elastic-elastic.org', + logRate: 'Chart type:bar chart', + pValue: '7.14e-9', + impact: 'High', + }, + { + fieldName: 'ip', + fieldValue: '30.156.16.163', + logRate: 'Chart type:bar chart', + pValue: '3.28e-13', + impact: 'High', + }, + { + fieldName: 'machine.os.keyword', + fieldValue: 'win xp', + logRate: 'Chart type:bar chart', + pValue: '0.0000997', + impact: 'Medium', + }, + { + fieldName: 'referer', + fieldValue: 'http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', + logRate: 'Chart type:bar chart', + pValue: '4.74e-13', + impact: 'High', + }, + { + fieldName: 'response.keyword', + fieldValue: '404', + logRate: 'Chart type:bar chart', + pValue: '0.00000604', + impact: 'Medium', + }, + ], + fieldSelectorPopover: [ + 'clientip', + 'geo.dest', + 'geo.srcdest', + 'host.keyword', + 'ip', + 'machine.os.keyword', + 'referer', + 'response.keyword', + ], + }, +}; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis_test_data.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis_test_data.ts index 3fb1e00b95201..c3f33ff2aa5ba 100644 --- a/x-pack/test/functional/apps/aiops/log_rate_analysis_test_data.ts +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis_test_data.ts @@ -5,289 +5,57 @@ * 2.0. */ -import { LOG_RATE_ANALYSIS_TYPE, type LogRateAnalysisType } from '@kbn/aiops-utils'; +import { LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-utils'; -import type { TestData } from './types'; - -export const kibanaLogsDataViewTestData: TestData = { - suiteTitle: 'kibana sample data logs', - analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, - dataGenerator: 'kibana_sample_data_logs', - isSavedSearch: false, - sourceIndexOrSavedSearch: 'kibana_sample_data_logs', - brushIntervalFactor: 1, - chartClickCoordinates: [235, 0], - fieldSelectorSearch: 'referer', - fieldSelectorApplyAvailable: true, - action: { - type: 'LogPatternAnalysis', - tableRowId: '1064853178', - expected: { - queryBar: - 'clientip:30.156.16.164 AND host.keyword:elastic-elastic-elastic.org AND ip:30.156.16.163 AND response.keyword:404 AND machine.os.keyword:win xp AND geo.dest:IN AND geo.srcdest:US\\:IN', - totalDocCount: 100, - }, - }, - expected: { - totalDocCountFormatted: '14,074', - analysisGroupsTable: [ - { - group: - '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* referer: http://www.elastic-elastic-elastic.com/success/timothy-l-kopra* response.keyword: 404Showing 5 out of 8 items. 8 items unique to this group.', - docCount: '100', - }, - ], - filteredAnalysisGroupsTable: [ - { - group: - '* clientip: 30.156.16.164* host.keyword: elastic-elastic-elastic.org* ip: 30.156.16.163* response.keyword: 404* machine.os.keyword: win xpShowing 5 out of 7 items. 7 items unique to this group.', - docCount: '100', - }, - ], - analysisTable: [ - { - fieldName: 'clientip', - fieldValue: '30.156.16.164', - logRate: 'Chart type:bar chart', - pValue: '3.10e-13', - impact: 'High', - }, - { - fieldName: 'geo.dest', - fieldValue: 'IN', - logRate: 'Chart type:bar chart', - pValue: '0.000716', - impact: 'Medium', - }, - { - fieldName: 'geo.srcdest', - fieldValue: 'US:IN', - logRate: 'Chart type:bar chart', - pValue: '0.000716', - impact: 'Medium', - }, - { - fieldName: 'host.keyword', - fieldValue: 'elastic-elastic-elastic.org', - logRate: 'Chart type:bar chart', - pValue: '7.14e-9', - impact: 'High', - }, - { - fieldName: 'ip', - fieldValue: '30.156.16.163', - logRate: 'Chart type:bar chart', - pValue: '3.28e-13', - impact: 'High', - }, - { - fieldName: 'machine.os.keyword', - fieldValue: 'win xp', - logRate: 'Chart type:bar chart', - pValue: '0.0000997', - impact: 'Medium', - }, - { - fieldName: 'referer', - fieldValue: 'http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', - logRate: 'Chart type:bar chart', - pValue: '4.74e-13', - impact: 'High', - }, - { - fieldName: 'response.keyword', - fieldValue: '404', - logRate: 'Chart type:bar chart', - pValue: '0.00000604', - impact: 'Medium', - }, - ], - fieldSelectorPopover: [ - 'clientip', - 'geo.dest', - 'geo.srcdest', - 'host.keyword', - 'ip', - 'machine.os.keyword', - 'referer', - 'response.keyword', - ], - }, -}; - -export const farequoteDataViewTestData: TestData = { - suiteTitle: 'farequote with spike', - analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, - dataGenerator: 'farequote_with_spike', - isSavedSearch: false, - sourceIndexOrSavedSearch: 'ft_farequote', - brushDeviationTargetTimestamp: 1455033600000, - brushIntervalFactor: 1, - chartClickCoordinates: [0, 0], - fieldSelectorSearch: 'airline', - fieldSelectorApplyAvailable: false, - expected: { - totalDocCountFormatted: '86,374', - sampleProbabilityFormatted: '0.5', - fieldSelectorPopover: ['airline', 'custom_field.keyword'], - }, -}; - -export const farequoteDataViewTestDataWithQuery: TestData = { - suiteTitle: 'farequote with spike', - analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, - dataGenerator: 'farequote_with_spike', - isSavedSearch: false, - sourceIndexOrSavedSearch: 'ft_farequote', - brushDeviationTargetTimestamp: 1455033600000, - brushIntervalFactor: 1, - chartClickCoordinates: [0, 0], - fieldSelectorSearch: 'airline', - fieldSelectorApplyAvailable: false, - query: 'NOT airline:("SWR" OR "ACA" OR "AWE" OR "BAW" OR "JAL" OR "JBU" OR "JZA" OR "KLM")', - expected: { - totalDocCountFormatted: '48,799', - analysisGroupsTable: [ - { - docCount: '297', - group: '* airline: AAL', - }, - { - docCount: '100', - group: '* custom_field.keyword: deviation* airline: UAL', - }, - ], - analysisTable: [ - { - fieldName: 'airline', - fieldValue: 'AAL', - logRate: 'Chart type:bar chart', - pValue: '1.18e-8', - impact: 'High', - }, - ], - fieldSelectorPopover: ['airline', 'custom_field.keyword'], - }, -}; +import { kibanaLogsDataViewTestData } from './log_rate_analysis/test_data/kibana_logs_data_view_test_data'; +import { farequoteDataViewTestData } from './log_rate_analysis/test_data/farequote_data_view_test_data'; +import { farequoteDataViewTestDataWithQuery } from './log_rate_analysis/test_data/farequote_data_view_test_data_with_query'; +import { getArtificialLogDataViewTestData } from './log_rate_analysis/test_data/artificial_log_data_view_test_data'; -const REFERENCE_TS = 1669018354793; -const DAY_MS = 86400000; - -const DEVIATION_TS = REFERENCE_TS - DAY_MS * 2; -const BASELINE_TS = DEVIATION_TS - DAY_MS * 1; - -const getArtificialLogDataViewTestData = ( - analysisType: LogRateAnalysisType, - textField: boolean -): TestData => ({ - suiteTitle: `artificial logs with ${analysisType} and ${ - textField ? 'text field' : 'no text field' - }`, - analysisType, - dataGenerator: `artificial_logs_with_${analysisType}_${textField ? 'textfield' : 'notextfield'}`, - isSavedSearch: false, - sourceIndexOrSavedSearch: `artificial_logs_with_${analysisType}_${ - textField ? 'textfield' : 'notextfield' - }`, - brushBaselineTargetTimestamp: BASELINE_TS + DAY_MS / 2, - brushDeviationTargetTimestamp: DEVIATION_TS + DAY_MS / 2, - brushIntervalFactor: 10, - chartClickCoordinates: [-200, 30], - fieldSelectorSearch: 'user', - fieldSelectorApplyAvailable: true, - expected: { - totalDocCountFormatted: '8,400', - analysisGroupsTable: [ - textField - ? { - group: 'message: an unexpected error occuredurl: home.phpresponse_code: 500', - docCount: '634', - } - : { - group: 'response_code: 500url: home.php', - docCount: '792', - }, - textField - ? { - group: 'message: an unexpected error occuredurl: login.phpresponse_code: 500', - docCount: '632', - } - : { - group: 'url: login.phpresponse_code: 500', - docCount: '790', - }, - { - docCount: '636', - group: 'user: Peterurl: home.php', - }, - { - docCount: '632', - group: 'user: Peterurl: login.php', - }, - ], - filteredAnalysisGroupsTable: textField - ? [ - { - group: '* url: home.phpmessage: an unexpected error occuredresponse_code: 500', - docCount: '634', - }, - { - group: '* url: login.phpmessage: an unexpected error occuredresponse_code: 500', - docCount: '632', - }, - ] - : [ - { group: '* url: home.phpresponse_code: 500', docCount: '792' }, - { group: '* url: login.phpresponse_code: 500', docCount: '790' }, - ], - analysisTable: [ - ...(textField - ? [ - { - fieldName: 'message', - fieldValue: 'an unexpected error occured', - logRate: 'Chart type:bar chart', - pValue: '0.00000100', - impact: 'Medium', - }, - { - fieldName: 'response_code', - fieldValue: '500', - logRate: 'Chart type:bar chart', - pValue: '3.61e-12', - impact: 'High', - }, - ] - : []), - { - fieldName: 'url', - fieldValue: 'home.php', - impact: 'Low', - logRate: 'Chart type:bar chart', - pValue: '0.00974', - }, - ...(textField - ? [] - : [ - { - fieldName: 'user', - fieldValue: 'Peter', - impact: 'High', - logRate: 'Chart type:bar chart', - pValue: '2.63e-21', - }, - ]), - ], - fieldSelectorPopover: [...(textField ? ['message'] : []), 'response_code', 'url', 'user'], - }, -}); +import type { TestData } from './types'; export const logRateAnalysisTestData: TestData[] = [ kibanaLogsDataViewTestData, farequoteDataViewTestData, farequoteDataViewTestDataWithQuery, - getArtificialLogDataViewTestData(LOG_RATE_ANALYSIS_TYPE.SPIKE, false), - getArtificialLogDataViewTestData(LOG_RATE_ANALYSIS_TYPE.SPIKE, true), - getArtificialLogDataViewTestData(LOG_RATE_ANALYSIS_TYPE.DIP, false), - getArtificialLogDataViewTestData(LOG_RATE_ANALYSIS_TYPE.DIP, true), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + textField: false, + zeroDocsFallback: false, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + textField: true, + zeroDocsFallback: false, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.DIP, + textField: false, + zeroDocsFallback: false, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.DIP, + textField: true, + zeroDocsFallback: false, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + textField: true, + zeroDocsFallback: true, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.SPIKE, + textField: false, + zeroDocsFallback: true, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.DIP, + textField: true, + zeroDocsFallback: true, + }), + getArtificialLogDataViewTestData({ + analysisType: LOG_RATE_ANALYSIS_TYPE.DIP, + textField: false, + zeroDocsFallback: true, + }), ]; diff --git a/x-pack/test/functional/apps/aiops/types.ts b/x-pack/test/functional/apps/aiops/types.ts index 45f376cb670e1..72a72b1d2bb37 100644 --- a/x-pack/test/functional/apps/aiops/types.ts +++ b/x-pack/test/functional/apps/aiops/types.ts @@ -8,7 +8,7 @@ import type { LogRateAnalysisType } from '@kbn/aiops-utils'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { LogRateAnalysisDataGenerator } from '../../services/aiops/log_rate_analysis_data_generator'; +import type { LogRateAnalysisDataGenerator } from '../../services/aiops/log_rate_analysis_data_generator'; interface TestDataTableActionLogPatternAnalysis { type: 'LogPatternAnalysis'; diff --git a/x-pack/test/functional/apps/discover/value_suggestions.ts b/x-pack/test/functional/apps/discover/value_suggestions.ts index 8d0c0ac5a969a..c17d4d803246c 100644 --- a/x-pack/test/functional/apps/discover/value_suggestions.ts +++ b/x-pack/test/functional/apps/discover/value_suggestions.ts @@ -25,7 +25,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); } - describe('value suggestions', function describeIndexTests() { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/172248 + describe.skip('value suggestions', function describeIndexTests() { before(async function () { await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); @@ -54,7 +55,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('discover'); }); - describe('discover', () => { + // FLAKY: https://github.com/elastic/kibana/issues/172246 + describe.skip('discover', () => { afterEach(async () => { await queryBar.clearQuery(); }); diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index a61a2abba1870..428ef13c34866 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -255,5 +255,76 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const data = await PageObjects.lens.getCurrentChartDebugStateForVizType('xyVisChart'); assertMatchesExpectedData(data!); }); + + it('should allow editing the query in the dashboard', async () => { + await PageObjects.discover.selectTextBaseLang(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await monacoEditor.setCodeEditorValue('from logstash-* | limit 10'); + await testSubjects.click('querySubmitButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click('TextBasedLangEditor-expand'); + // save the visualization + await testSubjects.click('unifiedHistogramSaveVisualization'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.saveModal('TextBasedChart1', false, false, false, 'new'); + await testSubjects.existOrFail('embeddablePanelHeading-TextBasedChart1'); + await elasticChart.setNewChartUiDebugFlag(true); + await PageObjects.header.waitUntilLoadingHasFinished(); + // open the inline editing flyout + await testSubjects.click('embeddablePanelToggleMenuIcon'); + await testSubjects.click('embeddablePanelAction-ACTION_CONFIGURE_IN_LENS'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + // change the query + await monacoEditor.setCodeEditorValue('from logstash-* | stats maxB = max(bytes)'); + await testSubjects.click('TextBasedLangEditor-run-query-button'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + expect((await PageObjects.lens.getMetricVisualizationData()).length).to.be.equal(1); + + // change the query to display a datatabler + await monacoEditor.setCodeEditorValue('from logstash-* | limit 10'); + await testSubjects.click('TextBasedLangEditor-run-query-button'); + await PageObjects.lens.waitForVisualization(); + expect(await testSubjects.exists('lnsDataTable')).to.be(true); + + await PageObjects.lens.removeDimension('lnsDatatable_metrics'); + await PageObjects.lens.removeDimension('lnsDatatable_metrics'); + await PageObjects.lens.removeDimension('lnsDatatable_metrics'); + await PageObjects.lens.removeDimension('lnsDatatable_metrics'); + + await PageObjects.lens.configureTextBasedLanguagesDimension({ + dimension: 'lnsDatatable_metrics > lns-empty-dimension', + field: 'bytes', + keepOpen: true, + }); + await testSubjects.click('lns-indexPattern-dimensionContainerBack'); + // click donut from suggestions + await testSubjects.click('lensSuggestionsPanelToggleButton'); + await testSubjects.click('lnsSuggestion-donut'); + expect(await testSubjects.exists('partitionVisChart')).to.be(true); + }); + + it('should default title when saving chart in Discover (even when modal is closed and reopened)', async () => { + await PageObjects.discover.selectTextBaseLang(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await monacoEditor.setCodeEditorValue( + 'from logstash-* | stats averageB = avg(bytes) by extension' + ); + await testSubjects.click('querySubmitButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.chooseLensChart('Bar vertical stacked'); + await testSubjects.click('TextBasedLangEditor-expand'); + await testSubjects.click('unifiedHistogramSaveVisualization'); + await PageObjects.header.waitUntilLoadingHasFinished(); + let title = await testSubjects.getAttribute('savedObjectTitle', 'value'); + expect(title).to.equal('Bar vertical stacked'); + await testSubjects.click('saveCancelButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click('unifiedHistogramSaveVisualization'); + await PageObjects.header.waitUntilLoadingHasFinished(); + title = await testSubjects.getAttribute('savedObjectTitle', 'value'); + expect(title).to.equal('Bar vertical stacked'); + }); }); } diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 27cc61ca0c273..6637b7d42d222 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -146,7 +146,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '0.8%' }, { metric: 'normalizedLoad1m', value: '1.4%' }, { metric: 'memoryUsage', value: '18.0%' }, - { metric: 'diskSpaceUsage', value: '17.5%' }, + { metric: 'diskUsage', value: '17.5%' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { await retry.tryForTime(3 * 1000, async () => { diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index 3a5b5a0156d5e..f06ac57e893cb 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -192,7 +192,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '13.9%' }, { metric: 'normalizedLoad1m', value: '18.8%' }, { metric: 'memoryUsage', value: '94.9%' }, - { metric: 'diskSpaceUsage', value: 'N/A' }, + { metric: 'diskUsage', value: 'N/A' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { await retry.try(async () => { @@ -397,7 +397,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '0.8%' }, { metric: 'normalizedLoad1m', value: '0.3%' }, { metric: 'memoryUsage', value: '16.8%' }, - { metric: 'diskSpaceUsage', value: '17.1%' }, + { metric: 'diskUsage', value: '17.1%' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { await retry.try(async () => { @@ -552,7 +552,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '0.8%' }, { metric: 'normalizedLoad1m', value: '0.2%' }, { metric: 'memoryUsage', value: '16.3%' }, - { metric: 'diskSpaceUsage', value: '16.9%' }, + { metric: 'diskUsage', value: '16.9%' }, ].map(async ({ metric, value }) => { await retry.try(async () => { const tileValue = await pageObjects.infraHostsView.getKPITileValue(metric); diff --git a/x-pack/test/functional/apps/infra/logs/ml_job_id_formats/tests.ts b/x-pack/test/functional/apps/infra/logs/ml_job_id_formats/tests.ts index ec16fe567f4ea..06ea712c7ccc0 100644 --- a/x-pack/test/functional/apps/infra/logs/ml_job_id_formats/tests.ts +++ b/x-pack/test/functional/apps/infra/logs/ml_job_id_formats/tests.ts @@ -30,9 +30,8 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { const requestTracker = createRequestTracker(browser, pageObjects.common); let mlJobHelper: MlJobHelper; - describe('ML job ID formats', function () { - this.tags('includeFirefox'); - + // Disabled until https://github.com/elastic/kibana/issues/171913 is addressed + describe.skip('ML job ID formats', function () { this.beforeAll(async () => { // Access to ml.api has to happen inside a test or test hook mlJobHelper = createMlJobHelper(ml.api); @@ -205,8 +204,7 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/171493 - describe.skip('creation and recreation', () => { + describe('creation and recreation', () => { it('create first ML job', async () => { await logsUi.logEntryRatePage.navigateTo(); await requestTracker.install(); diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index a824106c6f617..e4cfdaae3d227 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -169,7 +169,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '13.9%' }, { metric: 'normalizedLoad1m', value: '18.8%' }, { metric: 'memoryUsage', value: '94.9%' }, - { metric: 'diskSpaceUsage', value: 'N/A' }, + { metric: 'diskUsage', value: 'N/A' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { await retry.tryForTime(3 * 1000, async () => { @@ -366,7 +366,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'cpuUsage', value: '99.6%' }, { metric: 'normalizedLoad1m', value: '1,300.3%' }, { metric: 'memoryUsage', value: '42.2%' }, - { metric: 'diskSpaceUsage', value: '36.0%' }, + { metric: 'diskUsage', value: '36.0%' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { await retry.tryForTime(3 * 1000, async () => { diff --git a/x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js b/x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js index 081a1a41a93df..55e504e207d4a 100644 --- a/x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js +++ b/x-pack/test/functional/apps/maps/group4/mvt_geotile_grid.js @@ -53,7 +53,7 @@ export default function ({ getPageObjects, getService }) { index: 'logstash-*', gridPrecision: '8', renderAs: 'grid', - requestBody: `(aggs:(max_of_bytes:(max:(field:bytes))),fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z')))),must:!(),must_not:!(),should:!())),runtime_mappings:())`, + requestBody: `(aggs:(max_of_bytes:(max:(field:bytes))),fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z'))),(exists:(field:geo.coordinates))),must:!(),must_not:!(),should:!())),runtime_mappings:())`, }); //Should correctly load meta for style-rule (sigma is set to 1, opacity to 1) diff --git a/x-pack/test/functional/apps/maps/group4/mvt_scaling.js b/x-pack/test/functional/apps/maps/group4/mvt_scaling.js index cd779392ba821..e624ab39dee12 100644 --- a/x-pack/test/functional/apps/maps/group4/mvt_scaling.js +++ b/x-pack/test/functional/apps/maps/group4/mvt_scaling.js @@ -55,7 +55,7 @@ export default function ({ getPageObjects, getService }) { hasLabels: 'false', index: 'geo_shapes*', requestBody: - '(fields:!(prop1),query:(bool:(filter:!(),must:!(),must_not:!(),should:!())),runtime_mappings:(),size:10001)', + '(fields:!(prop1),query:(bool:(filter:!((exists:(field:geometry))),must:!(),must_not:!(),should:!())),runtime_mappings:(),size:10001)', }); }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts index 9927f37a6c8f9..19b48858b115b 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts @@ -40,7 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); await PageObjects.common.setTime({ from, to }); @@ -48,7 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); await PageObjects.common.unsetTime(); }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_embeddables_migration.ts b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_embeddables_migration.ts index 2760c3c136557..8e942d8e1e4b5 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_embeddables_migration.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_embeddables_migration.ts @@ -70,7 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/map_to_ml.ts b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/map_to_ml.ts index 3ce45876f77b1..f77c46de6341e 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/map_to_ml.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/map_to_ml.ts @@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.securityUI.loginAsMlPowerUser(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await kibanaServer.importExport.load(dashboardArchive); await browser.setWindowSize(1920, 1080); }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/advanced_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/advanced_job.ts index 2c9c9cb7a6b98..db5be20f2e978 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/advanced_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/advanced_job.ts @@ -199,8 +199,8 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/categorization_small'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); - await ml.testResources.createIndexPatternIfNeeded('ft_categorization_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_categorization_small', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -209,8 +209,8 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); - await ml.testResources.deleteIndexPatternByTitle('ft_categorization_small'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_categorization_small'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/categorization_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/categorization_job.ts index b1939d75d05c3..cbdc873286f07 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/categorization_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/categorization_job.ts @@ -86,7 +86,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/categorization_small'); - await ml.testResources.createIndexPatternIfNeeded('ft_categorization_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_categorization_small', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -95,7 +95,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_categorization_small'); + await ml.testResources.deleteDataViewByTitle('ft_categorization_small'); }); it('job creation loads the categorization wizard for the source data', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_jobs_to_advanced_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_jobs_to_advanced_job.ts index 49383315df1f9..4ca0079a0e438 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_jobs_to_advanced_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_jobs_to_advanced_job.ts @@ -209,8 +209,8 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/categorization_small'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); - await ml.testResources.createIndexPatternIfNeeded('ft_categorization_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_categorization_small', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -220,8 +220,8 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); - await ml.testResources.deleteIndexPatternByTitle('ft_categorization_small'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_categorization_small'); }); describe('from multi-metric job creation wizard', function () { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_single_metric_job_to_multi_metric.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_single_metric_job_to_multi_metric.ts index c469440030038..ba90e58524bc9 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_single_metric_job_to_multi_metric.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/convert_single_metric_job_to_multi_metric.ts @@ -16,16 +16,16 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; const remoteName = 'ftr-remote:'; - const indexPatternName = 'ft_farequote'; - const indexPatternString = config.get('esTestCluster.ccs') - ? remoteName + indexPatternName - : indexPatternName; + const esIndexPatternName = 'ft_farequote'; + const esIndexPatternString = config.get('esTestCluster.ccs') + ? remoteName + esIndexPatternName + : esIndexPatternName; describe('single metric job conversion to multi-metric job', function () { this.tags(['ml']); before(async () => { await esNode.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternString, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(esIndexPatternString, '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -34,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle(indexPatternString); + await ml.testResources.deleteDataViewByTitle(esIndexPatternString); }); const jobId = `fq_single_to_multi_${Date.now()}`; @@ -59,7 +59,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.jobManagement.navigateToNewJobSourceSelection(); await ml.testExecution.logTestStep('loads the job type selection page'); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(indexPatternString); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(esIndexPatternString); await ml.testExecution.logTestStep('loads the single metric job wizard page'); await ml.jobTypeSelection.selectSingleMetricJob(); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/custom_urls.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/custom_urls.ts index dd8b72c3504af..5a557d2270944 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/custom_urls.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/custom_urls.ts @@ -69,7 +69,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); testDashboardId = await ml.testResources.createMLTestDashboardIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -80,7 +80,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.testResources.deleteMLTestDashboard(); await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('opens the custom URLs tab in the edit job flyout', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/date_nanos_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/date_nanos_job.ts index d63f6af6c0486..864d89b2b08f3 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/date_nanos_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/date_nanos_job.ts @@ -94,17 +94,14 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/event_rate_nanos'); - await ml.testResources.createIndexPatternIfNeeded( - 'ft_event_rate_gen_trend_nanos', - '@timestamp' - ); + await ml.testResources.createDataViewIfNeeded('ft_event_rate_gen_trend_nanos', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_event_rate_gen_trend_nanos'); + await ml.testResources.deleteDataViewByTitle('ft_event_rate_gen_trend_nanos'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/delete_job_and_delete_annotations.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/delete_job_and_delete_annotations.ts index 13ab18934f942..ebfc468ff86d4 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/delete_job_and_delete_annotations.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/delete_job_and_delete_annotations.ts @@ -20,10 +20,10 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const remoteName = 'ftr-remote:'; - const indexPatternName = 'ft_farequote'; - const indexPatternString = config.get('esTestCluster.ccs') - ? remoteName + indexPatternName - : indexPatternName; + const esIndexPatternName = 'ft_farequote'; + const esIndexPatternString = config.get('esTestCluster.ccs') + ? remoteName + esIndexPatternName + : esIndexPatternName; const testSetupJobConfigs = [SINGLE_METRIC_JOB_CONFIG, MULTI_METRIC_JOB_CONFIG]; @@ -64,7 +64,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esNode.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternString, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(esIndexPatternString, '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle(indexPatternString); + await ml.testResources.deleteDataViewByTitle(esIndexPatternString); }); for (const { suiteTitle, jobId, deleteAnnotations, expectedAnnotations } of testConfigs) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/geo_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/geo_job.ts index daed44cce85fa..35d6d40cdbe27 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/geo_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/geo_job.ts @@ -84,7 +84,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -93,7 +93,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it('job creation loads the geo wizard for the source data', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/multi_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/multi_metric_job.ts index 9bbfdef22ce55..fa2e5d11eca0e 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/multi_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/multi_metric_job.ts @@ -84,7 +84,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -93,7 +93,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('job creation loads the multi metric wizard for the source data', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/population_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/population_job.ts index ea02186c45b74..5451ea16d8e4c 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/population_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/population_job.ts @@ -98,7 +98,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -107,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it('job creation loads the population wizard for the source data', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/saved_search_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/saved_search_job.ts index 7d9c528d763d7..b8493e2daa1b6 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/saved_search_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/saved_search_job.ts @@ -269,7 +269,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createSavedSearchFarequoteFilterIfNeeded(); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); @@ -283,7 +283,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts index b1575f0fa57ba..7ce5d1838a1a5 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts @@ -75,10 +75,10 @@ export default function ({ getService }: FtrProviderContext) { const calendarId = `wizard-test-calendar_${Date.now()}`; const remoteName = 'ftr-remote:'; - const indexPatternName = 'ft_farequote'; - const indexPatternString = config.get('esTestCluster.ccs') - ? remoteName + indexPatternName - : indexPatternName; + const esIndexPatternName = 'ft_farequote'; + const esIndexPatternString = config.get('esTestCluster.ccs') + ? remoteName + esIndexPatternName + : esIndexPatternName; const fieldStatsEntries = [ { @@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esNode.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternString, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(esIndexPatternString, '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createCalendar(calendarId); @@ -101,7 +101,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle(indexPatternString); + await ml.testResources.deleteDataViewByTitle(esIndexPatternString); }); it('job creation loads the single metric wizard for the source data', async () => { @@ -113,7 +113,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.jobManagement.navigateToNewJobSourceSelection(); await ml.testExecution.logTestStep('job creation loads the job type selection page'); - await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(indexPatternString); + await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(esIndexPatternString); await ml.testExecution.logTestStep('job creation loads the single metric job wizard page'); await ml.jobTypeSelection.selectSingleMetricJob(); @@ -230,7 +230,7 @@ export default function ({ getService }: FtrProviderContext) { it('job cloning creates a temporary data view and opens the single metric wizard if a matching data view does not exist', async () => { await ml.testExecution.logTestStep('delete data view used by job'); - await ml.testResources.deleteIndexPatternByTitle(indexPatternString); + await ml.testResources.deleteDataViewByTitle(esIndexPatternString); // Refresh page to ensure page has correct cache of data views await browser.refresh(); @@ -244,7 +244,7 @@ export default function ({ getService }: FtrProviderContext) { it('job cloning opens the existing job in the single metric wizard', async () => { await ml.testExecution.logTestStep('recreate data view used by job'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternString, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(esIndexPatternString, '@timestamp'); await ml.navigation.navigateToMl(); await ml.navigation.navigateToJobManagement(); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job_without_datafeed_start.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job_without_datafeed_start.ts index 4cdea1a726fe9..7d1724fbd0181 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job_without_datafeed_start.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job_without_datafeed_start.ts @@ -60,7 +60,7 @@ export default function ({ getService }: FtrProviderContext) { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -68,7 +68,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('job creation loads the single metric wizard for the source data', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/aggregated_scripted_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/aggregated_scripted_job.ts index 66261321ccb80..4d9fd0bbead81 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/aggregated_scripted_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/aggregated_scripted_job.ts @@ -364,16 +364,16 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); }); after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); }); for (const testData of supportedTestSuites) { describe(testData.suiteTitle, function () { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/annotations.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/annotations.ts index 05e38d565e969..ab1177d2dbc84 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/annotations.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/annotations.ts @@ -32,7 +32,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); const JOB_CONFIG = ml.commonConfig.getADFqSingleMetricJobConfig(jobId); @@ -47,7 +47,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); describe('creating', function () { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts index 29cc28993d6c2..a6e867676d10f 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts @@ -76,7 +76,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createMLTestDashboardIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await ml.testResources.deleteMLTestDashboard(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts index 93ec331230a8a..3a60e8fca97c2 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts @@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) { describe('with single metric job', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); @@ -54,7 +54,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('opens a job from job list link', async () => { diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts index 809ebf204e2a7..1779589a5a0c1 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/single_metric_viewer.ts @@ -46,7 +46,7 @@ export default function ({ getService }: FtrProviderContext) { describe('with single metric job', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); @@ -55,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('opens a job from job list link', async () => { @@ -134,7 +134,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob(jobConfig, datafeedConfig); await ml.securityUI.loginAsMlPowerUser(); @@ -142,7 +142,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it('opens a job from job list link', async () => { diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 6dd953a84e4d0..d042371951182 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -17,7 +17,7 @@ import { const testDiscoverCustomUrl: DiscoverUrlConfig = { label: 'Show data', - indexPattern: 'ft_bank_marketing', + indexName: 'ft_bank_marketing', queryEntityFieldNames: ['day'], timeRange: TIME_RANGE_TYPE.AUTO, }; @@ -43,7 +43,7 @@ export default function ({ getService }: FtrProviderContext) { let testDashboardId: string | null = null; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); - await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing'); + await ml.testResources.createDataViewIfNeeded('ft_bank_marketing'); await ml.testResources.setKibanaTimeZoneToUTC(); testDashboardId = await ml.testResources.createMLTestDashboardIfNeeded(); @@ -52,7 +52,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_bank_marketing'); + await ml.testResources.deleteDataViewByTitle('ft_bank_marketing'); }); const jobId = `bm_1_${Date.now()}`; @@ -76,7 +76,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'y', trainingPercent: 20, modelMemory: '60mb', - createIndexPattern: true, + createDataView: true, fieldStatsEntries: [ { fieldName: 'age', @@ -201,7 +201,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -314,6 +314,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -336,18 +340,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts index 1444c13192e56..307678384d470 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts @@ -18,7 +18,7 @@ export default function ({ getService }: FtrProviderContext) { describe.skip('classification saved search creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote_small', '@timestamp'); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); // Need to use the saved searches with filters that match multiple airlines @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + await ml.testResources.deleteDataViewByTitle('ft_farequote_small'); }); const dateNow = Date.now(); @@ -93,7 +93,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'airline', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { fieldStatsValues: { airline: ['AAL', 'AWE', 'ASA', 'ACA', 'AMX'] } as Record< string, @@ -193,7 +193,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'airline', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { fieldStatsValues: { airline: ['AAL', 'AWE', 'ASA', 'ACA', 'AMX'] } as Record< string, @@ -293,7 +293,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'airline', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { fieldStatsValues: { airline: ['AAL', 'ASA'], @@ -385,7 +385,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'airline', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { fieldStatsValues: { airline: ['ASA', 'FFT'] } as Record, source: 'ft_farequote_small', @@ -463,7 +463,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -576,6 +576,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -590,18 +594,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts index 19baea2c48c1f..da62f44fb305d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts @@ -19,7 +19,7 @@ export default function ({ getService }: FtrProviderContext) { const testDataList: Array<{ suiteTitle: string; archive: string; - indexPattern: { name: string; timeField: string }; + dataView: { name: string; timeField: string }; job: DeepPartial; }> = (() => { const timestamp = Date.now(); @@ -28,7 +28,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'classification job supported by the form', archive: 'x-pack/test/functional/es_archives/ml/bm_classification', - indexPattern: { name: 'ft_bank_marketing', timeField: '@timestamp' }, + dataView: { name: 'ft_bank_marketing', timeField: '@timestamp' }, job: { id: `bm_1_${timestamp}`, description: @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'outlier detection job supported by the form', archive: 'x-pack/test/functional/es_archives/ml/ihp_outlier', - indexPattern: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, + dataView: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, job: { id: `ihp_1_${timestamp}`, description: 'This is the job description', @@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'regression job supported by the form', archive: 'x-pack/test/functional/es_archives/ml/egs_regression', - indexPattern: { name: 'ft_egs_regression', timeField: '@timestamp' }, + dataView: { name: 'ft_egs_regression', timeField: '@timestamp' }, job: { id: `egs_1_${timestamp}`, description: 'This is the job description', @@ -141,9 +141,9 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded(testData.archive); - await ml.testResources.createIndexPatternIfNeeded( - testData.indexPattern.name, - testData.indexPattern.timeField + await ml.testResources.createDataViewIfNeeded( + testData.dataView.name, + testData.dataView.timeField ); await ml.api.createDataFrameAnalyticsJob(testData.job as DataFrameAnalyticsConfig); @@ -157,9 +157,9 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.deleteIndices(cloneDestIndex); await ml.api.deleteIndices(testData.job.dest!.index as string); - await ml.testResources.deleteIndexPatternByTitle(testData.job.dest!.index as string); - await ml.testResources.deleteIndexPatternByTitle(cloneDestIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.indexPattern.name); + await ml.testResources.deleteDataViewByTitle(testData.job.dest!.index as string); + await ml.testResources.deleteDataViewByTitle(cloneDestIndex); + await ml.testResources.deleteDataViewByTitle(testData.dataView.name); }); it('opens the existing job in the data frame analytics job wizard', async () => { diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/custom_urls.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/custom_urls.ts index a5dad1827d27d..dac2d1ae1fa36 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/custom_urls.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/custom_urls.ts @@ -15,7 +15,7 @@ import { const testDiscoverCustomUrl: DiscoverUrlConfig = { label: 'Show data', - indexPattern: 'ft_farequote', + indexName: 'ft_farequote', queryEntityFieldNames: ['airline'], timeRange: TIME_RANGE_TYPE.AUTO, }; @@ -70,7 +70,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); await ml.api.createAndRunDFAJob(dfaJobConfig); @@ -79,7 +79,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); await ml.api.deleteIndices('user-farequote_small'); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); describe('run custom urls', function () { diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 8b4514add92cb..c639b16dbb894 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -17,7 +17,7 @@ import { const testDiscoverCustomUrl: DiscoverUrlConfig = { label: 'Show data', - indexPattern: 'ft_ihp_outlier', + indexName: 'ft_ihp_outlier', queryEntityFieldNames: ['SaleType'], timeRange: TIME_RANGE_TYPE.AUTO, }; @@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier'); testDashboardId = await ml.testResources.createMLTestDashboardIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -53,7 +53,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'); + await ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'); }); const jobId = `ihp_1_${Date.now()}`; @@ -83,7 +83,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, modelMemory: '5mb', - createIndexPattern: true, + createDataView: true, advancedEditorContent: [ '{', ' "description": "Outlier detection job based on ft_ihp_outlier dataset with runtime fields",', @@ -192,7 +192,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -305,6 +305,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -322,18 +326,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts index a6f68a8eafd0b..93478ebbb766e 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation_saved_search.ts @@ -16,7 +16,7 @@ export default function ({ getService }: FtrProviderContext) { describe('outlier detection saved search creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote_small', '@timestamp'); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded( @@ -31,7 +31,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + await ml.testResources.deleteDataViewByTitle('ft_farequote_small'); }); const dateNow = Date.now(); @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, modelMemory: '1mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', histogramCharts: [ @@ -140,7 +140,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, modelMemory: '65mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', histogramCharts: [ @@ -217,7 +217,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, modelMemory: '65mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', histogramCharts: [ @@ -295,7 +295,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, modelMemory: '65mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', histogramCharts: [ @@ -362,7 +362,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -452,6 +452,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -461,18 +465,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts index b4ed75c35043a..6978452afdc32 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { const testDiscoverCustomUrl: DiscoverUrlConfig = { label: 'Show data', - indexPattern: 'ft_egs_regression', + indexName: 'ft_egs_regression', queryEntityFieldNames: ['stabf'], timeRange: TIME_RANGE_TYPE.AUTO, }; @@ -57,7 +57,7 @@ export default function ({ getService }: FtrProviderContext) { let testDashboardId: string | null = null; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression'); - await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression'); + await ml.testResources.createDataViewIfNeeded('ft_egs_regression'); await ml.testResources.setKibanaTimeZoneToUTC(); testDashboardId = await ml.testResources.createMLTestDashboardIfNeeded(); @@ -66,7 +66,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_egs_regression'); + await ml.testResources.deleteDataViewByTitle('ft_egs_regression'); }); const jobId = `egs_1_${Date.now()}`; @@ -90,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'stab', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, advancedEditorContent: [ '{', ' "description": "Regression job based on ft_egs_regression dataset with runtime fields",', @@ -197,7 +197,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -320,6 +320,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -337,18 +341,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts index 043f546dea69f..fb5702e98ac1d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation_saved_search.ts @@ -16,7 +16,7 @@ export default function ({ getService }: FtrProviderContext) { describe('regression saved search creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote_small'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote_small', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote_small', '@timestamp'); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded('ft_farequote_small'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded('ft_farequote_small'); await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded( @@ -31,7 +31,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote_small'); + await ml.testResources.deleteDataViewByTitle('ft_farequote_small'); }); const dateNow = Date.now(); @@ -73,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'responsetime', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], @@ -161,7 +161,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'responsetime', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], @@ -249,7 +249,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'responsetime', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], @@ -331,7 +331,7 @@ export default function ({ getService }: FtrProviderContext) { dependentVariable: 'responsetime', trainingPercent: 20, modelMemory: '20mb', - createIndexPattern: true, + createDataView: true, expected: { source: 'ft_farequote_small', runtimeFieldsEditorContent: ['{', ' "uppercase_airline": {', ' "type": "keyword",'], @@ -401,7 +401,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await ml.api.deleteIndices(testData.destinationIndex); - await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await ml.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the data frame analytics wizard', async () => { @@ -487,6 +487,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists(); await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex); + await ml.testExecution.logTestStep('displays the create data view switch'); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchExists(); + await ml.dataFrameAnalyticsCreation.assertCreateDataViewSwitchCheckState(true); + await ml.testExecution.logTestStep('continues to the validation step'); await ml.dataFrameAnalyticsCreation.continueToValidationStep(); @@ -496,18 +500,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); - - await ml.testExecution.logTestStep('sets the create data view switch'); - await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists(); - await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState( - testData.createIndexPattern - ); }); it('runs the analytics job and displays it correctly in the job list', async () => { await ml.testExecution.logTestStep('creates and starts the analytics job'); await ml.dataFrameAnalyticsCreation.assertCreateButtonExists(); - await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true); + await ml.dataFrameAnalyticsCreation.assertStartJobSwitchCheckState(true); await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId); await ml.testExecution.logTestStep('finishes analytics processing'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts index 77cae02f3cfcb..7ccf329222a55 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/results_view_content.ts @@ -18,7 +18,7 @@ export default function ({ getService }: FtrProviderContext) { const testDataList: Array<{ suiteTitle: string; archive: string; - indexPattern: { name: string; timeField: string }; + dataView: { name: string; timeField: string }; job: DeepPartial; sortBy: { column: string; @@ -38,7 +38,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'binary classification job', archive: 'x-pack/test/functional/es_archives/ml/ihp_outlier', - indexPattern: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, + dataView: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, job: { id: `ihp_fi_binary_${timestamp}`, description: @@ -107,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'multi class classification job', archive: 'x-pack/test/functional/es_archives/ml/ihp_outlier', - indexPattern: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, + dataView: { name: 'ft_ihp_outlier', timeField: '@timestamp' }, job: { id: `ihp_fi_multi_${timestamp}`, description: @@ -178,7 +178,7 @@ export default function ({ getService }: FtrProviderContext) { { suiteTitle: 'regression job', archive: 'x-pack/test/functional/es_archives/ml/egs_regression', - indexPattern: { name: 'ft_egs_regression', timeField: '@timestamp' }, + dataView: { name: 'ft_egs_regression', timeField: '@timestamp' }, job: { id: `egs_fi_reg_${timestamp}`, description: 'This is the job description', @@ -252,9 +252,9 @@ export default function ({ getService }: FtrProviderContext) { await ml.securityUI.loginAsMlPowerUser(); for (const testData of testDataList) { await esArchiver.loadIfNeeded(testData.archive); - await ml.testResources.createIndexPatternIfNeeded( - testData.indexPattern.name, - testData.indexPattern.timeField + await ml.testResources.createDataViewIfNeeded( + testData.dataView.name, + testData.dataView.timeField ); await ml.api.createAndRunDFAJob(testData.job as DataFrameAnalyticsConfig); } @@ -263,7 +263,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); for (const testData of testDataList) { - await ml.testResources.deleteIndexPatternByTitle(testData.indexPattern.name); + await ml.testResources.deleteDataViewByTitle(testData.dataView.name); } }); @@ -273,13 +273,13 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToMl(); await ml.navigation.navigateToDataFrameAnalytics(); await ml.dataFrameAnalyticsTable.waitForAnalyticsToLoad(); - await ml.testResources.createIndexPatternIfNeeded(testData.job.dest!.index as string); + await ml.testResources.createDataViewIfNeeded(testData.job.dest!.index as string); await ml.dataFrameAnalyticsTable.openResultsView(testData.job.id as string); }); after(async () => { await ml.api.deleteIndices(testData.job.dest!.index as string); - await ml.testResources.deleteIndexPatternByTitle(testData.job.dest!.index as string); + await ml.testResources.deleteDataViewByTitle(testData.job.dest!.index as string); }); it('should display the total feature importance in the results view', async () => { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/data_drift.ts b/x-pack/test/functional/apps/ml/data_visualizer/data_drift.ts index 5195997c3ba31..bec8999104410 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/data_drift.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/data_drift.ts @@ -84,10 +84,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('data drift', async function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -97,10 +97,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/ml/ihp_outlier'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await Promise.all([ - ml.testResources.deleteIndexPatternByTitle('ft_fare*'), - ml.testResources.deleteIndexPatternByTitle('ft_fare*,ft_fareq*'), - ml.testResources.deleteIndexPatternByTitle('ft_farequote'), - ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'), + ml.testResources.deleteDataViewByTitle('ft_fare*'), + ml.testResources.deleteDataViewByTitle('ft_fare*,ft_fareq*'), + ml.testResources.deleteDataViewByTitle('ft_farequote'), + ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'), ]); }); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 073023ed6b5dc..f0185fc371006 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -26,7 +26,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the index data visualizer page` @@ -147,8 +147,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_module_sample_logs', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded(); @@ -159,8 +159,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { after(async () => { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_logs'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); }); describe('with farequote', function () { @@ -223,7 +223,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the index data visualizer page` diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts index c7e00f8ed5b54..6f259a8120d28 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_actions_panel.ts @@ -14,7 +14,7 @@ export default function ({ getService }: FtrProviderContext) { describe('index based actions panel on trial license', function () { this.tags(['ml']); - const indexPatternName = 'ft_farequote'; + const esIndexName = 'ft_farequote'; const advancedJobWizardDatafeedQuery = JSON.stringify( { @@ -33,7 +33,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternName, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(esIndexName, '@timestamp'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -42,7 +42,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle(indexPatternName); + await ml.testResources.deleteDataViewByTitle(esIndexName); }); describe('create advanced job action', function () { @@ -52,10 +52,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToDataVisualizer(); await ml.testExecution.logTestStep('loads the saved search selection page'); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep('loads the index data visualizer page'); - await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(indexPatternName); + await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(esIndexName); }); it('opens the advanced job wizard', async () => { @@ -84,7 +84,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToDataVisualizer(); await ml.testExecution.logTestStep('loads the saved search selection page'); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep('loads the index data visualizer page'); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(savedSearch); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_data_view_management.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_data_view_management.ts index 6f15f5a33274e..28611dbcb8a1b 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_data_view_management.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_data_view_management.ts @@ -118,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the index data visualizer page` @@ -182,12 +182,12 @@ export default function ({ getService }: FtrProviderContext) { }); beforeEach(async () => { - await ml.testResources.createIndexPatternIfNeeded(indexPatternTitle, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(indexPatternTitle, '@timestamp'); await navigateToIndexDataVisualizer(originalTestData); }); afterEach(async () => { - await ml.testResources.deleteIndexPatternByTitle(indexPatternTitle); + await ml.testResources.deleteDataViewByTitle(indexPatternTitle); }); it(`adds new field`, async () => { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_filters.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_filters.ts index be8c06028685f..4be111e00bc85 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_filters.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_filters.ts @@ -54,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the index data visualizer page` @@ -91,7 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the saved search selection page` ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep( `${testData.suiteTitle} loads the index data visualizer page` @@ -124,7 +124,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('data visualizer with pinned global filters', function () { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded(); await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded(); @@ -133,7 +133,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async function () { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); describe(`with ${farequoteDataViewTestData.suiteTitle}`, function () { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_dashboard.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_dashboard.ts index 47c6e7725686d..7d38012c3651b 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_dashboard.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_dashboard.ts @@ -115,7 +115,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('field statistics in Dashboard', function () { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded(); await ml.securityUI.loginAsMlPowerUser(); }); @@ -123,7 +123,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async function () { await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); runTests(farequoteLuceneFiltersSearchTestData); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts index c0c7a47414ce5..34a8d59cc2147 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts @@ -96,8 +96,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_module_sample_logs', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded(); @@ -109,8 +109,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async function () { await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_logs'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); }); describe('when enabled', function () { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_random_sampler.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_random_sampler.ts index ce74bbfb8884a..e5fee365ff08e 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_random_sampler.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_random_sampler.ts @@ -17,7 +17,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.navigation.navigateToDataVisualizer(); await ml.testExecution.logTestStep(`loads the saved search selection page`); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep(`loads the index data visualizer page`); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(sourceIndexOrSavedSearch); @@ -28,8 +28,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_module_sample_logs', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -40,8 +40,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { after(async () => { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_logs'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); await browser.removeLocalStorageItem('dataVisualizer.randomSamplerPreference'); }); diff --git a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts index 1c289dd66f0e7..b986d29c7fee7 100644 --- a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts @@ -132,9 +132,9 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/module_sample_ecommerce' ); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createDataViewIfNeeded(ecIndexPattern, 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob( @@ -170,9 +170,9 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.deleteCalendar(calendarId); await ml.api.deleteFilter(filterId); await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'); - await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'); + await ml.testResources.deleteDataViewByTitle(ecIndexPattern); }); for (const testUser of testUsers) { @@ -387,7 +387,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should load an index into the data visualizer page' ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); await ml.testExecution.logTestStep('should display the time range step'); diff --git a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts index 960c3a6a4b0b8..7ffdf2286439d 100644 --- a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts @@ -138,9 +138,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/module_sample_ecommerce' ); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createDataViewIfNeeded(ecIndexPattern, 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.api.createAndRunAnomalyDetectionLookbackJob( @@ -176,9 +176,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.api.deleteCalendar(calendarId); await ml.api.deleteFilter(filterId); await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'); - await ml.testResources.deleteIndexPatternByTitle(ecIndexPattern); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'); + await ml.testResources.deleteDataViewByTitle(ecIndexPattern); }); for (const testUser of testUsers) { @@ -378,7 +378,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should load an index into the data visualizer page' ); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); await ml.testExecution.logTestStep('should display the time range step'); diff --git a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts index 5a50a34b4ee17..ebcdfbeb4b170 100644 --- a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts +++ b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts @@ -120,7 +120,7 @@ export default function ({ getService }: FtrProviderContext) { // Need to delete index before ingest pipeline, else it will give error await ml.api.deleteIngestPipeline(modelWithPipelineAndDestIndex.modelId); - await ml.testResources.deleteIndexPatternByTitle( + await ml.testResources.deleteDataViewByTitle( modelWithPipelineAndDestIndexExpectedValues.dataViewTitle ); }); diff --git a/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts b/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts index 062b2809c375d..e4511615a51a2 100644 --- a/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts +++ b/x-pack/test/functional/apps/ml/short_tests/notifications/notification_list.ts @@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Notifications list', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -54,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await spacesService.delete(idSpace1); await ml.api.cleanMlIndices(); await ml.testResources.cleanMLSavedObjects(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('displays a generic notification indicator', async () => { diff --git a/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts index 91121528d477a..32fe57b442753 100644 --- a/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_creation.ts @@ -18,7 +18,7 @@ export default function ({ getService }: FtrProviderContext) { describe('calendar creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await asyncForEach(jobConfigs, async (jobConfig) => { // @ts-expect-error not full interface @@ -30,7 +30,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); afterEach(async () => { diff --git a/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts index 9f68ccc8a1196..f7c1497b2b56e 100644 --- a/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts +++ b/x-pack/test/functional/apps/ml/short_tests/settings/calendar_edit.ts @@ -24,7 +24,7 @@ export default function ({ getService }: FtrProviderContext) { describe('calendar edit', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await asyncForEach(jobConfigs, async (jobConfig) => { // @ts-expect-error not full interface @@ -43,7 +43,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); afterEach(async () => { diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts index 6976b4db4eda6..0ddb7da85c526 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts @@ -257,16 +257,16 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await ml.api.cleanMlIndices(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); - await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_bank_marketing', '@timestamp'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier', '@timestamp'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression'); - await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_egs_regression', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -289,10 +289,10 @@ export default function ({ getService }: FtrProviderContext) { 'anomaly_detection_jobs', 'data_frame_analytics_jobs', ]); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_bank_marketing'); - await ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'); - await ml.testResources.deleteIndexPatternByTitle('ft_egs_regression'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_bank_marketing'); + await ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'); + await ml.testResources.deleteDataViewByTitle('ft_egs_regression'); }); it('opens export flyout and exports anomaly detector jobs', async () => { diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts index b4782e37596e3..8a3fa058fe253 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/import_jobs.ts @@ -36,8 +36,8 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.cleanMlIndices(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_bank_marketing', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -47,8 +47,8 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await ml.api.cleanMlIndices(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_bank_marketing'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_bank_marketing'); }); for (const testData of testDataListPositive) { diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts index 55b2bdfa12063..06ec8e19b10c3 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts @@ -111,8 +111,8 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -137,8 +137,8 @@ export default function ({ getService }: FtrProviderContext) { } await ml.api.cleanMlIndices(); await ml.testResources.cleanMLSavedObjects([spaceIds.idSpace1]); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_ihp_outlier'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_ihp_outlier'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts index 317a71ae79a0b..42a462d259812 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/synchronize.ts @@ -24,7 +24,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -40,7 +40,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.deleteDataFrameAnalyticsJobES(dfaJobId); } await ml.testResources.cleanMLSavedObjects(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); }); it('should have nothing to sync initially', async () => { diff --git a/x-pack/test/functional/apps/transform/actions/deleting.ts b/x-pack/test/functional/apps/transform/actions/deleting.ts index 80a00e0b09dfd..911c656e9fd5f 100644 --- a/x-pack/test/functional/apps/transform/actions/deleting.ts +++ b/x-pack/test/functional/apps/transform/actions/deleting.ts @@ -65,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); for (const testData of testDataList) { await transform.api.createAndRunTransform( @@ -80,11 +80,11 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { for (const testData of testDataList) { - await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index); + await transform.testResources.deleteDataViewByTitle(testData.originalConfig.dest.index); await transform.api.deleteIndices(testData.originalConfig.dest.index); } await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/transform/actions/reauthorizing.ts b/x-pack/test/functional/apps/transform/actions/reauthorizing.ts index 9f731f6947cbd..f256e902eac97 100644 --- a/x-pack/test/functional/apps/transform/actions/reauthorizing.ts +++ b/x-pack/test/functional/apps/transform/actions/reauthorizing.ts @@ -138,7 +138,7 @@ export default function ({ getService }: FtrProviderContext) { ); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); for (const testData of testDataList) { await transform.api.createTransform(testData.originalConfig.id, testData.originalConfig, { @@ -157,12 +157,12 @@ export default function ({ getService }: FtrProviderContext) { await transform.securityCommon.clearAllTransformApiKeys(); for (const testData of testDataList) { - await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index); + await transform.testResources.deleteDataViewByTitle(testData.originalConfig.dest.index); await transform.api.deleteIndices(testData.originalConfig.dest.index); } await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/transform/actions/resetting.ts b/x-pack/test/functional/apps/transform/actions/resetting.ts index c6e5180dfa8fb..b62772f63454b 100644 --- a/x-pack/test/functional/apps/transform/actions/resetting.ts +++ b/x-pack/test/functional/apps/transform/actions/resetting.ts @@ -67,7 +67,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); for (const testData of testDataList) { await transform.api.createAndRunTransform( @@ -82,11 +82,11 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { for (const testData of testDataList) { - await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index); + await transform.testResources.deleteDataViewByTitle(testData.originalConfig.dest.index); await transform.api.deleteIndices(testData.originalConfig.dest.index); } await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/transform/actions/starting.ts b/x-pack/test/functional/apps/transform/actions/starting.ts index 2cccd4525856d..2ecd345ac0e1a 100644 --- a/x-pack/test/functional/apps/transform/actions/starting.ts +++ b/x-pack/test/functional/apps/transform/actions/starting.ts @@ -110,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); for (const testData of testDataList) { if ( @@ -135,12 +135,12 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { for (const testData of testDataList) { - await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index); + await transform.testResources.deleteDataViewByTitle(testData.originalConfig.dest.index); await transform.api.deleteIndices(testData.originalConfig.dest.index); } await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); for (const testData of testDataList) { diff --git a/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts b/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts index e56d06e15a625..24a3874eddec0 100644 --- a/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts +++ b/x-pack/test/functional/apps/transform/creation/index_pattern/continuous_transform.ts @@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); const DEFAULT_NUM_FAILURE_RETRIES = '5'; @@ -326,7 +326,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { await transform.api.deleteIndices(testData.destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await transform.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the wizard for the source data', async () => { @@ -440,9 +440,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionValue(''); await transform.wizard.setTransformDescription(testData.transformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); - await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.assertDestinationIndexValue(testData.transformId); await transform.wizard.setDestinationIndex(testData.destinationIndex); await transform.testExecution.logTestStep('displays the create data view switch'); diff --git a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts index 69406c5830421..9e5c1e4f1b081 100644 --- a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts @@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('creation_index_pattern', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); @@ -33,7 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); await transform.securityUI.logout(); }); @@ -509,7 +509,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await transform.api.deleteIndices(testData.destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await transform.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the wizard for the source data', async () => { @@ -727,9 +727,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionValue(''); await transform.wizard.setTransformDescription(testData.transformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); - await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.assertDestinationIndexValue(testData.transformId); await transform.wizard.setDestinationIndex(testData.destinationIndex); await transform.testExecution.logTestStep('displays the create data view switch'); diff --git a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts index 3f0adc5783893..bbdf53b3eda5c 100644 --- a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts +++ b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts @@ -37,7 +37,7 @@ export default function ({ getService }: FtrProviderContext) { describe('creation with runtime mappings', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await transform.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await transform.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); @@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_farequote'); + await transform.testResources.deleteDataViewByTitle('ft_farequote'); }); const histogramCharts: HistogramCharts = [ @@ -254,7 +254,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await transform.api.deleteIndices(testData.destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await transform.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the wizard for the source data', async () => { @@ -406,9 +406,16 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionValue(''); await transform.wizard.setTransformDescription(testData.transformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); - await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.assertDestinationIndexValue(testData.transformId); await transform.wizard.setDestinationIndex(testData.destinationIndex); await transform.testExecution.logTestStep('displays the create data view switch'); diff --git a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts index c976be55a7885..c85a1a88b429c 100644 --- a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts +++ b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts @@ -23,7 +23,7 @@ export default function ({ getService }: FtrProviderContext) { describe('creation_saved_search', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await transform.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await transform.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await transform.testResources.createSavedSearchFarequoteFilterIfNeeded(); await transform.testResources.setKibanaTimeZoneToUTC(); @@ -33,7 +33,7 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { await transform.api.cleanTransformIndices(); await transform.testResources.deleteSavedSearches(); - await transform.testResources.deleteIndexPatternByTitle('ft_farequote'); + await transform.testResources.deleteDataViewByTitle('ft_farequote'); }); const testDataList: Array = [ @@ -121,7 +121,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await transform.api.deleteIndices(testData.destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await transform.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('loads the wizard for the source data', async () => { @@ -239,9 +239,16 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertTransformDescriptionValue(''); await transform.wizard.setTransformDescription(testData.transformDescription); - await transform.testExecution.logTestStep('inputs the destination index'); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); - await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.assertDestinationIndexValue(testData.transformId); await transform.wizard.setDestinationIndex(testData.destinationIndex); await transform.testExecution.logTestStep('displays the create data view switch'); diff --git a/x-pack/test/functional/apps/transform/edit_clone/cloning.ts b/x-pack/test/functional/apps/transform/edit_clone/cloning.ts index caa6552024e14..3146548b8bbcc 100644 --- a/x-pack/test/functional/apps/transform/edit_clone/cloning.ts +++ b/x-pack/test/functional/apps/transform/edit_clone/cloning.ts @@ -199,7 +199,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await transform.api.createAndRunTransform( transformConfigWithPivot.id, transformConfigWithPivot @@ -223,17 +223,17 @@ export default function ({ getService }: FtrProviderContext) { }); after(async () => { - await transform.testResources.deleteIndexPatternByTitle(transformConfigWithPivot.dest.index); - await transform.testResources.deleteIndexPatternByTitle( + await transform.testResources.deleteDataViewByTitle(transformConfigWithPivot.dest.index); + await transform.testResources.deleteDataViewByTitle( transformConfigWithRuntimeMapping.dest.index ); - await transform.testResources.deleteIndexPatternByTitle(transformConfigWithLatest.dest.index); + await transform.testResources.deleteDataViewByTitle(transformConfigWithLatest.dest.index); await transform.api.deleteIndices(transformConfigWithPivot.dest.index); await transform.api.deleteIndices(transformConfigWithRuntimeMapping.dest.index); await transform.api.deleteIndices(transformConfigWithLatest.dest.index); await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); const testDataList: TestData[] = [ @@ -397,7 +397,7 @@ export default function ({ getService }: FtrProviderContext) { describe(`${testData.suiteTitle}`, function () { after(async () => { await transform.api.deleteIndices(testData.destinationIndex); - await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex); + await transform.testResources.deleteDataViewByTitle(testData.destinationIndex); }); it('opens the existing transform in the wizard', async () => { @@ -517,9 +517,16 @@ export default function ({ getService }: FtrProviderContext) { ); await transform.wizard.setTransformDescription(testData.transformDescription); + await transform.testExecution.logTestStep( + 'should default the set destination index to job id switch to true' + ); + await transform.wizard.assertDestIndexSameAsIdSwitchExists(); + await transform.wizard.assertDestIndexSameAsIdCheckState(true); + await transform.testExecution.logTestStep('should input the destination index'); + await transform.wizard.setDestIndexSameAsIdCheckState(false); await transform.wizard.assertDestinationIndexInputExists(); - await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.assertDestinationIndexValue(testData.transformId); await transform.wizard.setDestinationIndex(testData.destinationIndex); await transform.testExecution.logTestStep('should display the create data view switch'); diff --git a/x-pack/test/functional/apps/transform/edit_clone/editing.ts b/x-pack/test/functional/apps/transform/edit_clone/editing.ts index 10cacc361779c..1a8394ca9a668 100644 --- a/x-pack/test/functional/apps/transform/edit_clone/editing.ts +++ b/x-pack/test/functional/apps/transform/edit_clone/editing.ts @@ -27,7 +27,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await transform.api.createAndRunTransform( transformConfigWithPivot.id, @@ -43,12 +43,12 @@ export default function ({ getService }: FtrProviderContext) { }); after(async () => { - await transform.testResources.deleteIndexPatternByTitle(transformConfigWithPivot.dest.index); + await transform.testResources.deleteDataViewByTitle(transformConfigWithPivot.dest.index); await transform.api.deleteIndices(transformConfigWithPivot.dest.index); - await transform.testResources.deleteIndexPatternByTitle(transformConfigWithLatest.dest.index); + await transform.testResources.deleteDataViewByTitle(transformConfigWithLatest.dest.index); await transform.api.deleteIndices(transformConfigWithLatest.dest.index); await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); const testDataList = [ diff --git a/x-pack/test/functional/apps/transform/permissions/full_transform_access.ts b/x-pack/test/functional/apps/transform/permissions/full_transform_access.ts index 2e7779767eaca..0d745a5123995 100644 --- a/x-pack/test/functional/apps/transform/permissions/full_transform_access.ts +++ b/x-pack/test/functional/apps/transform/permissions/full_transform_access.ts @@ -55,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await transform.api.createAndRunTransform( transformConfigWithPivot.id, @@ -67,12 +67,10 @@ export default function ({ getService }: FtrProviderContext) { }); after(async () => { - await transform.testResources.deleteIndexPatternByTitle( - transformConfigWithPivot.dest.index - ); + await transform.testResources.deleteDataViewByTitle(transformConfigWithPivot.dest.index); await transform.api.deleteIndices(transformConfigWithPivot.dest.index); await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it('should display elements in the Transform list page correctly', async () => { diff --git a/x-pack/test/functional/apps/transform/permissions/read_transform_access.ts b/x-pack/test/functional/apps/transform/permissions/read_transform_access.ts index 4e715d4f07467..02cfcfdfba003 100644 --- a/x-pack/test/functional/apps/transform/permissions/read_transform_access.ts +++ b/x-pack/test/functional/apps/transform/permissions/read_transform_access.ts @@ -55,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await transform.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await transform.api.createAndRunTransform( transformConfigWithPivot.id, @@ -67,12 +67,10 @@ export default function ({ getService }: FtrProviderContext) { }); after(async () => { - await transform.testResources.deleteIndexPatternByTitle( - transformConfigWithPivot.dest.index - ); + await transform.testResources.deleteDataViewByTitle(transformConfigWithPivot.dest.index); await transform.api.deleteIndices(transformConfigWithPivot.dest.index); await transform.api.cleanTransformIndices(); - await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await transform.testResources.deleteDataViewByTitle('ft_ecommerce'); }); it('should display elements in the Transform list page correctly', async () => { diff --git a/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/data.json b/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/data.json new file mode 100644 index 0000000000000..a56ba3832c67b --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/data.json @@ -0,0 +1,1268 @@ +{ + "type": "doc", + "value": { + "id": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1", + "index": ".alerts-security.alerts-default", + "source": { + "@timestamp": "2023-04-27T11:03:57.906Z", + "Endpoint": { + "capabilities": [ + "isolation", + "kill_process", + "suspend_process", + "running_processes", + "get_file", + "execute" + ], + "configuration": { + "isolation": true + }, + "policy": { + "applied": { + "endpoint_policy_version": 3, + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", + "name": "With Eventing", + "status": "success", + "version": 5 + } + }, + "state": { + "isolation": true + }, + "status": "enrolled" + }, + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb", + "type": "endpoint", + "version": "8.8.0" + }, + "data_stream": { + "dataset": "endpoint.alerts", + "namespace": "default", + "type": "logs" + }, + "dll": [ + { + "Ext": { + "compile_time": 1534424710, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 5362483200, + "mapped_size": 0 + }, + "code_signature": { + "subject_name": "Cybereason Inc", + "trusted": true + }, + "hash": { + "md5": "1f2d082566b0fc5f2c238a5180db7451", + "sha1": "ca85243c0af6a6471bdaa560685c51eefd6dbc0d", + "sha256": "8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2" + }, + "path": "C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe", + "pe": { + "architecture": "x64" + } + } + ], + "ecs": { + "version": "1.4.0" + }, + "elastic": { + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb" + } + }, + "event.action": "creation", + "event.agent_id_status": "auth_metadata_missing", + "event.category": "malware", + "event.code": "malicious_file", + "event.dataset": "endpoint", + "event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "event.ingested": "2023-04-27T10:58:03Z", + "event.kind": "signal", + "event.module": "endpoint", + "event.sequence": 5826, + "event.type": "creation", + "file": { + "Ext": { + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "malware_classification": { + "identifier": "endpointpe", + "score": 1, + "threshold": 0.66, + "version": "3.0.33" + }, + "quarantine_message": "fake quarantine message", + "quarantine_result": true, + "temp_file_path": "C:/temp/fake_malware.exe" + }, + "accessed": 1682752652103, + "created": 1682752652103, + "hash": { + "md5": "fake file md5", + "sha1": "fake file sha1", + "sha256": "fake file sha256" + }, + "mtime": 1682752652103, + "name": "fake_malware.exe", + "owner": "SYSTEM", + "path": "C:/fake_malware.exe", + "size": 3456 + }, + "user": { + "name": "user1" + }, + "host": { + "architecture": "wtnozeqvub", + "hostname": "Host-fwarau82er", + "id": "4260adf9-5e63-445d-92c6-e03359bcd342", + "ip": [ + "10.249.37.72", + "10.150.39.243", + "10.186.17.170" + ], + "mac": [ + "f5-f-97-dc-20-67", + "b5-56-ca-98-81-ca", + "22-86-39-4c-87-33" + ], + "name": "Host-fwarau82er", + "os": { + "Ext": { + "variant": "Darwin" + }, + "family": "Darwin", + "full": "macOS Monterey", + "name": "macOS", + "platform": "macOS", + "version": "12.6.1" + } + }, + "kibana.alert.ancestors": [ + { + "depth": 0, + "id": "vT9cwocBh3b8EMpD8lsi", + "index": ".ds-logs-endpoint.alerts-default-2023.04.27-000001", + "type": "event" + } + ], + "kibana.alert.depth": 1, + "kibana.alert.last_detected": "2023-04-27T11:03:57.993Z", + "kibana.alert.original_event.action": "creation", + "kibana.alert.original_event.agent_id_status": "auth_metadata_missing", + "kibana.alert.original_event.category": "malware", + "kibana.alert.original_event.code": "malicious_file", + "kibana.alert.original_event.dataset": "endpoint", + "kibana.alert.original_event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "kibana.alert.original_event.ingested": "2023-04-27T10:58:03Z", + "kibana.alert.original_event.kind": "alert", + "kibana.alert.original_event.module": "endpoint", + "kibana.alert.original_event.sequence": 5826, + "kibana.alert.original_event.type": "creation", + "kibana.alert.original_time": "2023-04-29T07:17:32.103Z", + "kibana.alert.reason": "malware event with process malware writer, file fake_malware.exe, on Host-fwarau82er created medium alert Endpoint Security.", + "kibana.alert.risk_score": 47, + "kibana.alert.rule.actions": [ + ], + "kibana.alert.rule.author": [ + "Elastic" + ], + "kibana.alert.rule.category": "Custom Query Rule", + "kibana.alert.rule.consumer": "siem", + "kibana.alert.rule.created_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.created_by": "elastic", + "kibana.alert.rule.description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "kibana.alert.rule.enabled": true, + "kibana.alert.rule.exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "kibana.alert.rule.execution.uuid": "ebf843ff-e0e1-47f8-9ed2-cc8066afbcef", + "kibana.alert.rule.false_positives": [ + ], + "kibana.alert.rule.from": "now-10m", + "kibana.alert.rule.immutable": true, + "kibana.alert.rule.indices": [ + "logs-endpoint.alerts-*" + ], + "kibana.alert.rule.interval": "5m", + "kibana.alert.rule.license": "Elastic License v2", + "kibana.alert.rule.max_signals": 10000, + "kibana.alert.rule.name": "Endpoint Security", + "kibana.alert.rule.parameters": { + "author": [ + "Elastic" + ], + "description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "false_positives": [ + ], + "from": "now-10m", + "immutable": true, + "index": [ + "logs-endpoint.alerts-*" + ], + "language": "kuery", + "license": "Elastic License v2", + "max_signals": 10000, + "query": "event.kind:alert and event.module:(endpoint and not endgame)\n", + "references": [ + ], + "related_integrations": [ + { + "package": "endpoint", + "version": "^8.2.0" + } + ], + "required_fields": [ + { + "ecs": true, + "name": "event.kind", + "type": "keyword" + }, + { + "ecs": true, + "name": "event.module", + "type": "keyword" + } + ], + "risk_score": 47, + "risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "rule_name_override": "message", + "setup": "", + "severity": "medium", + "severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "threat": [ + ], + "timestamp_override": "event.ingested", + "to": "now", + "type": "query", + "version": 101 + }, + "kibana.alert.rule.producer": "siem", + "kibana.alert.rule.references": [ + ], + "kibana.alert.rule.revision": 0, + "kibana.alert.rule.risk_score": 47, + "kibana.alert.rule.risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "kibana.alert.rule.rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "kibana.alert.rule.rule_name_override": "message", + "kibana.alert.rule.rule_type_id": "siem.queryRule", + "kibana.alert.rule.severity": "medium", + "kibana.alert.rule.severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "kibana.alert.rule.tags": [ + "Elastic", + "Endpoint Security" + ], + "kibana.alert.rule.threat": [ + ], + "kibana.alert.rule.timestamp_override": "event.ingested", + "kibana.alert.rule.to": "now", + "kibana.alert.rule.type": "query", + "kibana.alert.rule.updated_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.updated_by": "elastic", + "kibana.alert.rule.uuid": "7015a3e2-e4ea-11ed-8c11-49608884878f", + "kibana.alert.rule.version": 101, + "kibana.alert.severity": "medium", + "kibana.alert.start": "2023-04-27T11:03:57.993Z", + "kibana.alert.status": "active", + "kibana.alert.url": "http://localhost:5601/app/security/alerts/redirect/eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1?index=.alerts-security.alerts-default×tamp=2023-04-27T11:03:57.906Z", + "kibana.alert.uuid": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1", + "kibana.alert.workflow_status": "open", + "kibana.space_ids": [ + "default" + ], + "kibana.version": "8.8.0", + "process": { + "Ext": { + "ancestry": [ + "qa5jgw1wr7", + "5k1hclygc6" + ], + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "token": { + "domain": "NT AUTHORITY", + "integrity_level": 16384, + "integrity_level_name": "system", + "privileges": [ + { + "description": "Replace a process level token", + "enabled": false, + "name": "SeAssignPrimaryTokenPrivilege" + } + ], + "sid": "S-1-5-18", + "type": "tokenPrimary", + "user": "SYSTEM" + }, + "user": "SYSTEM" + }, + "entity_id": "nqh8ts6ves", + "entry_leader": { + "entity_id": "jnm38bel0w", + "name": "fake entry", + "pid": 791 + }, + "executable": "C:/malware.exe", + "group_leader": { + "entity_id": "jnm38bel0w", + "name": "fake leader", + "pid": 848 + }, + "hash": { + "md5": "fake md5", + "sha1": "fake sha1", + "sha256": "fake sha256" + }, + "name": "malware writer", + "parent": { + "entity_id": "qa5jgw1wr7", + "pid": 1 + }, + "pid": 2, + "session_leader": { + "entity_id": "jnm38bel0w", + "name": "fake session", + "pid": 909 + }, + "start": 1682752652103, + "uptime": 0 + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "170865e675eda76202f0095b23869d8d0726df4c91a343876df38b566bf1e57d", + "index": ".alerts-security.alerts-default", + "source": { + "@timestamp": "2023-04-27T11:03:57.906Z", + "Endpoint": { + "capabilities": [ + "isolation", + "kill_process", + "suspend_process", + "running_processes", + "get_file", + "execute" + ], + "configuration": { + "isolation": true + }, + "policy": { + "applied": { + "endpoint_policy_version": 3, + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", + "name": "With Eventing", + "status": "success", + "version": 5 + } + }, + "state": { + "isolation": true + }, + "status": "enrolled" + }, + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb", + "type": "endpoint", + "version": "8.8.0" + }, + "data_stream": { + "dataset": "endpoint.alerts", + "namespace": "default", + "type": "logs" + }, + "dll": [ + { + "Ext": { + "compile_time": 1534424710, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 5362483200, + "mapped_size": 0 + }, + "code_signature": { + "subject_name": "Cybereason Inc", + "trusted": true + }, + "hash": { + "md5": "1f2d082566b0fc5f2c238a5180db7451", + "sha1": "ca85243c0af6a6471bdaa560685c51eefd6dbc0d", + "sha256": "8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2" + }, + "path": "C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe", + "pe": { + "architecture": "x64" + } + } + ], + "ecs": { + "version": "1.4.0" + }, + "elastic": { + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb" + } + }, + "event.action": "creation", + "event.agent_id_status": "auth_metadata_missing", + "event.category": "malware", + "event.code": "malicious_file", + "event.dataset": "endpoint", + "event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "event.ingested": "2023-04-27T10:58:03Z", + "event.kind": "signal", + "event.module": "endpoint", + "event.sequence": 5826, + "event.type": "creation", + "file": { + "Ext": { + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "malware_classification": { + "identifier": "endpointpe", + "score": 1, + "threshold": 0.66, + "version": "3.0.33" + }, + "quarantine_message": "fake quarantine message", + "quarantine_result": true, + "temp_file_path": "C:/temp/fake_malware.exe" + }, + "accessed": 1682752652103, + "created": 1682752652103, + "hash": { + "md5": "fake file md5", + "sha1": "fake file sha1", + "sha256": "fake file sha256" + }, + "mtime": 1682752652103, + "name": "fake_malware.exe", + "owner": "SYSTEM", + "path": "C:/fake_malware.exe", + "size": 3456 + }, + "user": { + "name": "user1" + }, + "host": { + "architecture": "wtnozeqvub", + "hostname": "Host-fwarau82er", + "id": "4260adf9-5e63-445d-92c6-e03359bcd342", + "ip": [ + "10.249.37.72", + "10.150.39.243", + "10.186.17.170" + ], + "mac": [ + "f5-f-97-dc-20-67", + "b5-56-ca-98-81-ca", + "22-86-39-4c-87-33" + ], + "name": "Host-fwarau82er", + "os": { + "Ext": { + "variant": "Darwin" + }, + "family": "Darwin", + "full": "macOS Monterey", + "name": "macOS", + "platform": "macOS", + "version": "12.6.1" + } + }, + "kibana.alert.ancestors": [ + { + "depth": 0, + "id": "vT9cwocBh3b8EMpD8lsi", + "index": ".ds-logs-endpoint.alerts-default-2023.04.27-000001", + "type": "event" + } + ], + "kibana.alert.depth": 1, + "kibana.alert.last_detected": "2023-04-27T11:03:57.993Z", + "kibana.alert.original_event.action": "creation", + "kibana.alert.original_event.agent_id_status": "auth_metadata_missing", + "kibana.alert.original_event.category": "malware", + "kibana.alert.original_event.code": "malicious_file", + "kibana.alert.original_event.dataset": "endpoint", + "kibana.alert.original_event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "kibana.alert.original_event.ingested": "2023-04-27T10:58:03Z", + "kibana.alert.original_event.kind": "alert", + "kibana.alert.original_event.module": "endpoint", + "kibana.alert.original_event.sequence": 5826, + "kibana.alert.original_event.type": "creation", + "kibana.alert.original_time": "2023-04-29T07:17:32.103Z", + "kibana.alert.reason": "malware event with process malware writer, file fake_malware.exe, on Host-fwarau82er created medium alert Endpoint Security.", + "kibana.alert.risk_score": 47, + "kibana.alert.rule.actions": [ + ], + "kibana.alert.rule.author": [ + "Elastic" + ], + "kibana.alert.rule.category": "Custom Query Rule", + "kibana.alert.rule.consumer": "siem", + "kibana.alert.rule.created_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.created_by": "elastic", + "kibana.alert.rule.description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "kibana.alert.rule.enabled": true, + "kibana.alert.rule.exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "kibana.alert.rule.execution.uuid": "ebf843ff-e0e1-47f8-9ed2-cc8066afbcef", + "kibana.alert.rule.false_positives": [ + ], + "kibana.alert.rule.from": "now-10m", + "kibana.alert.rule.immutable": true, + "kibana.alert.rule.indices": [ + "logs-endpoint.alerts-*" + ], + "kibana.alert.rule.interval": "5m", + "kibana.alert.rule.license": "Elastic License v2", + "kibana.alert.rule.max_signals": 10000, + "kibana.alert.rule.name": "Endpoint Security", + "kibana.alert.rule.parameters": { + "author": [ + "Elastic" + ], + "description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "false_positives": [ + ], + "from": "now-10m", + "immutable": true, + "index": [ + "logs-endpoint.alerts-*" + ], + "language": "kuery", + "license": "Elastic License v2", + "max_signals": 10000, + "query": "event.kind:alert and event.module:(endpoint and not endgame)\n", + "references": [ + ], + "related_integrations": [ + { + "package": "endpoint", + "version": "^8.2.0" + } + ], + "required_fields": [ + { + "ecs": true, + "name": "event.kind", + "type": "keyword" + }, + { + "ecs": true, + "name": "event.module", + "type": "keyword" + } + ], + "risk_score": 47, + "risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "rule_name_override": "message", + "setup": "", + "severity": "medium", + "severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "threat": [ + ], + "timestamp_override": "event.ingested", + "to": "now", + "type": "query", + "version": 101 + }, + "kibana.alert.rule.producer": "siem", + "kibana.alert.rule.references": [ + ], + "kibana.alert.rule.revision": 0, + "kibana.alert.rule.risk_score": 47, + "kibana.alert.rule.risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "kibana.alert.rule.rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "kibana.alert.rule.rule_name_override": "message", + "kibana.alert.rule.rule_type_id": "siem.queryRule", + "kibana.alert.rule.severity": "medium", + "kibana.alert.rule.severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "kibana.alert.rule.tags": [ + "Elastic", + "Endpoint Security" + ], + "kibana.alert.rule.threat": [ + ], + "kibana.alert.rule.timestamp_override": "event.ingested", + "kibana.alert.rule.to": "now", + "kibana.alert.rule.type": "query", + "kibana.alert.rule.updated_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.updated_by": "elastic", + "kibana.alert.rule.uuid": "7015a3e2-e4ea-11ed-8c11-49608884878f", + "kibana.alert.rule.version": 101, + "kibana.alert.severity": "medium", + "kibana.alert.start": "2023-04-27T11:03:57.993Z", + "kibana.alert.status": "active", + "kibana.alert.url": "http://localhost:5601/app/security/alerts/redirect/eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1?index=.alerts-security.alerts-default×tamp=2023-04-27T11:03:57.906Z", + "kibana.alert.uuid": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1", + "kibana.alert.workflow_status": "open", + "kibana.space_ids": [ + "default" + ], + "kibana.version": "8.8.0", + "process": { + "Ext": { + "ancestry": [ + "qa5jgw1wr7", + "5k1hclygc6" + ], + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "token": { + "domain": "NT AUTHORITY", + "integrity_level": 16384, + "integrity_level_name": "system", + "privileges": [ + { + "description": "Replace a process level token", + "enabled": false, + "name": "SeAssignPrimaryTokenPrivilege" + } + ], + "sid": "S-1-5-18", + "type": "tokenPrimary", + "user": "SYSTEM" + }, + "user": "SYSTEM" + }, + "entity_id": "nqh8ts6ves", + "entry_leader": { + "entity_id": "jnm38bel0w", + "name": "fake entry", + "pid": 791 + }, + "executable": "C:/malware.exe", + "group_leader": { + "entity_id": "jnm38bel0w", + "name": "fake leader", + "pid": 848 + }, + "hash": { + "md5": "fake md5", + "sha1": "fake sha1", + "sha256": "fake sha256" + }, + "name": "malware writer", + "parent": { + "entity_id": "qa5jgw1wr7", + "pid": 1 + }, + "pid": 2, + "session_leader": { + "entity_id": "jnm38bel0w", + "name": "fake session", + "pid": 909 + }, + "start": 1682752652103, + "uptime": 0 + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "f3bbdf17847c703e37dca942dc6c1db69eb8af18a74c1f52b6d0bd76c6b3b135", + "index": ".alerts-security.alerts-default", + "source": { + "@timestamp": "2023-04-27T11:03:57.906Z", + "Endpoint": { + "capabilities": [ + "isolation", + "kill_process", + "suspend_process", + "running_processes", + "get_file", + "execute" + ], + "configuration": { + "isolation": true + }, + "policy": { + "applied": { + "endpoint_policy_version": 3, + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", + "name": "With Eventing", + "status": "success", + "version": 5 + } + }, + "state": { + "isolation": true + }, + "status": "enrolled" + }, + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb", + "type": "endpoint", + "version": "8.8.0" + }, + "data_stream": { + "dataset": "endpoint.alerts", + "namespace": "default", + "type": "logs" + }, + "dll": [ + { + "Ext": { + "compile_time": 1534424710, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 5362483200, + "mapped_size": 0 + }, + "code_signature": { + "subject_name": "Cybereason Inc", + "trusted": true + }, + "hash": { + "md5": "1f2d082566b0fc5f2c238a5180db7451", + "sha1": "ca85243c0af6a6471bdaa560685c51eefd6dbc0d", + "sha256": "8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2" + }, + "path": "C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe", + "pe": { + "architecture": "x64" + } + } + ], + "ecs": { + "version": "1.4.0" + }, + "elastic": { + "agent": { + "id": "b563ce99-e373-4a1f-a5fe-97e956140aeb" + } + }, + "event.action": "creation", + "event.agent_id_status": "auth_metadata_missing", + "event.category": "malware", + "event.code": "malicious_file", + "event.dataset": "endpoint", + "event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "event.ingested": "2023-04-27T10:58:03Z", + "event.kind": "signal", + "event.module": "endpoint", + "event.sequence": 5826, + "event.type": "creation", + "file": { + "Ext": { + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "malware_classification": { + "identifier": "endpointpe", + "score": 1, + "threshold": 0.66, + "version": "3.0.33" + }, + "quarantine_message": "fake quarantine message", + "quarantine_result": true, + "temp_file_path": "C:/temp/fake_malware.exe" + }, + "accessed": 1682752652103, + "created": 1682752652103, + "hash": { + "md5": "fake file md5", + "sha1": "fake file sha1", + "sha256": "fake file sha256" + }, + "mtime": 1682752652103, + "name": "fake_malware.exe", + "owner": "SYSTEM", + "path": "C:/fake_malware.exe", + "size": 3456 + }, + "user": { + "name": "user1" + }, + "host": { + "architecture": "wtnozeqvub", + "hostname": "Host-fwarau82er", + "id": "4260adf9-5e63-445d-92c6-e03359bcd342", + "ip": [ + "10.249.37.72", + "10.150.39.243", + "10.186.17.170" + ], + "mac": [ + "f5-f-97-dc-20-67", + "b5-56-ca-98-81-ca", + "22-86-39-4c-87-33" + ], + "name": "Host-fwarau82er", + "os": { + "Ext": { + "variant": "Darwin" + }, + "family": "Darwin", + "full": "macOS Monterey", + "name": "macOS", + "platform": "macOS", + "version": "12.6.1" + } + }, + "kibana.alert.ancestors": [ + { + "depth": 0, + "id": "vT9cwocBh3b8EMpD8lsi", + "index": ".ds-logs-endpoint.alerts-default-2023.04.27-000001", + "type": "event" + } + ], + "kibana.alert.depth": 1, + "kibana.alert.last_detected": "2023-04-27T11:03:57.993Z", + "kibana.alert.original_event.action": "creation", + "kibana.alert.original_event.agent_id_status": "auth_metadata_missing", + "kibana.alert.original_event.category": "malware", + "kibana.alert.original_event.code": "malicious_file", + "kibana.alert.original_event.dataset": "endpoint", + "kibana.alert.original_event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae", + "kibana.alert.original_event.ingested": "2023-04-27T10:58:03Z", + "kibana.alert.original_event.kind": "alert", + "kibana.alert.original_event.module": "endpoint", + "kibana.alert.original_event.sequence": 5826, + "kibana.alert.original_event.type": "creation", + "kibana.alert.original_time": "2023-04-29T07:17:32.103Z", + "kibana.alert.reason": "malware event with process malware writer, file fake_malware.exe, on Host-fwarau82er created medium alert Endpoint Security.", + "kibana.alert.risk_score": 47, + "kibana.alert.rule.actions": [ + ], + "kibana.alert.rule.author": [ + "Elastic" + ], + "kibana.alert.rule.category": "Custom Query Rule", + "kibana.alert.rule.consumer": "siem", + "kibana.alert.rule.created_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.created_by": "elastic", + "kibana.alert.rule.description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "kibana.alert.rule.enabled": true, + "kibana.alert.rule.exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "kibana.alert.rule.execution.uuid": "ebf843ff-e0e1-47f8-9ed2-cc8066afbcef", + "kibana.alert.rule.false_positives": [ + ], + "kibana.alert.rule.from": "now-10m", + "kibana.alert.rule.immutable": true, + "kibana.alert.rule.indices": [ + "logs-endpoint.alerts-*" + ], + "kibana.alert.rule.interval": "5m", + "kibana.alert.rule.license": "Elastic License v2", + "kibana.alert.rule.max_signals": 10000, + "kibana.alert.rule.name": "Endpoint Security", + "kibana.alert.rule.parameters": { + "author": [ + "Elastic" + ], + "description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.", + "exceptions_list": [ + { + "id": "endpoint_list", + "list_id": "endpoint_list", + "namespace_type": "agnostic", + "type": "endpoint" + } + ], + "false_positives": [ + ], + "from": "now-10m", + "immutable": true, + "index": [ + "logs-endpoint.alerts-*" + ], + "language": "kuery", + "license": "Elastic License v2", + "max_signals": 10000, + "query": "event.kind:alert and event.module:(endpoint and not endgame)\n", + "references": [ + ], + "related_integrations": [ + { + "package": "endpoint", + "version": "^8.2.0" + } + ], + "required_fields": [ + { + "ecs": true, + "name": "event.kind", + "type": "keyword" + }, + { + "ecs": true, + "name": "event.module", + "type": "keyword" + } + ], + "risk_score": 47, + "risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "rule_name_override": "message", + "setup": "", + "severity": "medium", + "severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "threat": [ + ], + "timestamp_override": "event.ingested", + "to": "now", + "type": "query", + "version": 101 + }, + "kibana.alert.rule.producer": "siem", + "kibana.alert.rule.references": [ + ], + "kibana.alert.rule.revision": 0, + "kibana.alert.rule.risk_score": 47, + "kibana.alert.rule.risk_score_mapping": [ + { + "field": "event.risk_score", + "operator": "equals", + "value": "" + } + ], + "kibana.alert.rule.rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306", + "kibana.alert.rule.rule_name_override": "message", + "kibana.alert.rule.rule_type_id": "siem.queryRule", + "kibana.alert.rule.severity": "medium", + "kibana.alert.rule.severity_mapping": [ + { + "field": "event.severity", + "operator": "equals", + "severity": "low", + "value": "21" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "medium", + "value": "47" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "high", + "value": "73" + }, + { + "field": "event.severity", + "operator": "equals", + "severity": "critical", + "value": "99" + } + ], + "kibana.alert.rule.tags": [ + "Elastic", + "Endpoint Security" + ], + "kibana.alert.rule.threat": [ + ], + "kibana.alert.rule.timestamp_override": "event.ingested", + "kibana.alert.rule.to": "now", + "kibana.alert.rule.type": "query", + "kibana.alert.rule.updated_at": "2023-04-27T10:58:27.546Z", + "kibana.alert.rule.updated_by": "elastic", + "kibana.alert.rule.uuid": "7015a3e2-e4ea-11ed-8c11-49608884878f", + "kibana.alert.rule.version": 101, + "kibana.alert.severity": "medium", + "kibana.alert.start": "2023-04-27T11:03:57.993Z", + "kibana.alert.status": "active", + "kibana.alert.url": "http://localhost:5601/app/security/alerts/redirect/eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1?index=.alerts-security.alerts-default×tamp=2023-04-27T11:03:57.906Z", + "kibana.alert.uuid": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1", + "kibana.alert.workflow_status": "open", + "kibana.space_ids": [ + "default" + ], + "kibana.version": "8.8.0", + "process": { + "Ext": { + "ancestry": [ + "qa5jgw1wr7", + "5k1hclygc6" + ], + "code_signature": [ + { + "subject_name": "bad signer", + "trusted": false + } + ], + "token": { + "domain": "NT AUTHORITY", + "integrity_level": 16384, + "integrity_level_name": "system", + "privileges": [ + { + "description": "Replace a process level token", + "enabled": false, + "name": "SeAssignPrimaryTokenPrivilege" + } + ], + "sid": "S-1-5-18", + "type": "tokenPrimary", + "user": "SYSTEM" + }, + "user": "SYSTEM" + }, + "entity_id": "nqh8ts6ves", + "entry_leader": { + "entity_id": "jnm38bel0w", + "name": "fake entry", + "pid": 791 + }, + "executable": "C:/malware.exe", + "group_leader": { + "entity_id": "jnm38bel0w", + "name": "fake leader", + "pid": 848 + }, + "hash": { + "md5": "fake md5", + "sha1": "fake sha1", + "sha256": "fake sha256" + }, + "name": "malware writer", + "parent": { + "entity_id": "qa5jgw1wr7", + "pid": 1 + }, + "pid": 2, + "session_leader": { + "entity_id": "jnm38bel0w", + "name": "fake session", + "pid": 909 + }, + "start": 1682752652103, + "uptime": 0 + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/mappings.json b/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/mappings.json new file mode 100644 index 0000000000000..f5a6e53cc4d60 --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs/mappings.json @@ -0,0 +1,7900 @@ +{ + "type": "index", + "value": { + "aliases": { + ".alerts-security.alerts-default": { + "is_write_index": true + }, + ".siem-signals-default": { + "is_write_index": false + } + }, + "index": ".internal.alerts-security.alerts-default-000001", + "mappings": { + "_meta": { + "kibana": { + "version": "8.8.0" + }, + "managed": true, + "namespace": "default" + }, + "dynamic": "false", + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "build": { + "properties": { + "original": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "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" + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "target": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "container": { + "properties": { + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "hash": { + "properties": { + "all": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "memory": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "device": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "manufacturer": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "properties": { + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "type": "keyword" + } + } + }, + "email": { + "properties": { + "attachments": { + "properties": { + "file": { + "properties": { + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + } + } + } + }, + "type": "nested" + }, + "bcc": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cc": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "content_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "delivery_timestamp": { + "type": "date" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "from": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "local_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message_id": { + "type": "wildcard" + }, + "origination_timestamp": { + "type": "date" + }, + "reply_to": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "sender": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "subject": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "to": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x_mailer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "type": "match_only_text" + }, + "stack_trace": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "type": "keyword" + }, + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "faas": { + "properties": { + "coldstart": { + "type": "boolean" + }, + "execution": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trigger": { + "properties": { + "request_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "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": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boot": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pid_ns_ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "calculated_score": { + "type": "float" + }, + "calculated_score_norm": { + "type": "float" + }, + "static_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "static_score": { + "type": "float" + }, + "static_score_norm": { + "type": "float" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + } + } + }, + "bytes": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + } + } + }, + "bytes": { + "type": "long" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kibana": { + "properties": { + "alert": { + "properties": { + "action_group": { + "type": "keyword" + }, + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "building_block_type": { + "type": "keyword" + }, + "case_ids": { + "type": "keyword" + }, + "depth": { + "type": "long" + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + }, + "end": { + "type": "date" + }, + "flapping": { + "type": "boolean" + }, + "flapping_history": { + "type": "boolean" + }, + "group": { + "properties": { + "id": { + "type": "keyword" + }, + "index": { + "type": "integer" + } + } + }, + "instance": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "last_detected": { + "type": "date" + }, + "maintenance_window_ids": { + "type": "keyword" + }, + "new_terms": { + "type": "keyword" + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "agent_id_status": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "keyword" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "type": "keyword" + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "url": { + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "reason": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "rule": { + "properties": { + "author": { + "type": "keyword" + }, + "building_block_type": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "exceptions_list": { + "type": "object" + }, + "execution": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "false_positives": { + "type": "keyword" + }, + "from": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "license": { + "type": "keyword" + }, + "max_signals": { + "type": "long" + }, + "name": { + "type": "keyword" + }, + "note": { + "type": "keyword" + }, + "parameters": { + "ignore_above": 4096, + "type": "flattened" + }, + "producer": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "revision": { + "type": "long" + }, + "rule_id": { + "type": "keyword" + }, + "rule_name_override": { + "type": "keyword" + }, + "rule_type_id": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "timestamp_override": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "uuid": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "severity": { + "type": "keyword" + }, + "start": { + "type": "date" + }, + "status": { + "type": "keyword" + }, + "suppression": { + "properties": { + "docs_count": { + "type": "long" + }, + "end": { + "type": "date" + }, + "start": { + "type": "date" + }, + "terms": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + }, + "system_status": { + "type": "keyword" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "from": { + "type": "date" + }, + "terms": { + "properties": { + "field": { + "type": "keyword" + }, + "value": { + "type": "keyword" + } + } + } + } + }, + "time_range": { + "format": "epoch_millis||strict_date_optional_time", + "type": "date_range" + }, + "url": { + "ignore_above": 2048, + "index": false, + "type": "keyword" + }, + "uuid": { + "type": "keyword" + }, + "workflow_reason": { + "type": "keyword" + }, + "workflow_status": { + "type": "keyword" + }, + "workflow_user": { + "type": "keyword" + } + } + }, + "space_ids": { + "type": "keyword" + }, + "version": { + "type": "version" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "file": { + "properties": { + "path": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "syslog": { + "properties": { + "appname": { + "ignore_above": 1024, + "type": "keyword" + }, + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "msgid": { + "ignore_above": 1024, + "type": "keyword" + }, + "priority": { + "type": "long" + }, + "procid": { + "ignore_above": 1024, + "type": "keyword" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "structured_data": { + "type": "flattened" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "message": { + "type": "match_only_text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "observer": { + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "orchestrator": { + "properties": { + "api_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "entry_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "attested_groups": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "attested_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "entry_meta": { + "properties": { + "source": { + "properties": { + "ip": { + "type": "ip" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "session_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + } + } + }, + "start": { + "type": "date" + } + } + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "env_vars": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "io": { + "properties": { + "bytes_skipped": { + "properties": { + "length": { + "type": "long" + }, + "offset": { + "type": "long" + } + } + }, + "max_bytes_per_process_exceeded": { + "type": "boolean" + }, + "text": { + "type": "wildcard" + }, + "total_bytes_captured": { + "type": "long" + }, + "total_bytes_skipped": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "previous": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "session_leader": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interactive": { + "type": "boolean" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "session_leader": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + } + } + }, + "start": { + "type": "date" + } + } + }, + "pid": { + "type": "long" + }, + "real_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "real_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "same_as_process": { + "type": "boolean" + }, + "saved_group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "saved_user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + } + } + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "start": { + "type": "date" + }, + "supplemental_groups": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "properties": { + "char_device": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + }, + "columns": { + "type": "long" + }, + "rows": { + "type": "long" + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "working_directory": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "author": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "path": "kibana.alert.ancestors.depth", + "type": "alias" + }, + "id": { + "path": "kibana.alert.ancestors.id", + "type": "alias" + }, + "index": { + "path": "kibana.alert.ancestors.index", + "type": "alias" + }, + "type": { + "path": "kibana.alert.ancestors.type", + "type": "alias" + } + } + }, + "depth": { + "path": "kibana.alert.depth", + "type": "alias" + }, + "group": { + "properties": { + "id": { + "path": "kibana.alert.group.id", + "type": "alias" + }, + "index": { + "path": "kibana.alert.group.index", + "type": "alias" + } + } + }, + "original_event": { + "properties": { + "action": { + "path": "kibana.alert.original_event.action", + "type": "alias" + }, + "category": { + "path": "kibana.alert.original_event.category", + "type": "alias" + }, + "code": { + "path": "kibana.alert.original_event.code", + "type": "alias" + }, + "created": { + "path": "kibana.alert.original_event.created", + "type": "alias" + }, + "dataset": { + "path": "kibana.alert.original_event.dataset", + "type": "alias" + }, + "duration": { + "path": "kibana.alert.original_event.duration", + "type": "alias" + }, + "end": { + "path": "kibana.alert.original_event.end", + "type": "alias" + }, + "hash": { + "path": "kibana.alert.original_event.hash", + "type": "alias" + }, + "id": { + "path": "kibana.alert.original_event.id", + "type": "alias" + }, + "kind": { + "path": "kibana.alert.original_event.kind", + "type": "alias" + }, + "module": { + "path": "kibana.alert.original_event.module", + "type": "alias" + }, + "outcome": { + "path": "kibana.alert.original_event.outcome", + "type": "alias" + }, + "provider": { + "path": "kibana.alert.original_event.provider", + "type": "alias" + }, + "reason": { + "path": "kibana.alert.original_event.reason", + "type": "alias" + }, + "risk_score": { + "path": "kibana.alert.original_event.risk_score", + "type": "alias" + }, + "risk_score_norm": { + "path": "kibana.alert.original_event.risk_score_norm", + "type": "alias" + }, + "sequence": { + "path": "kibana.alert.original_event.sequence", + "type": "alias" + }, + "severity": { + "path": "kibana.alert.original_event.severity", + "type": "alias" + }, + "start": { + "path": "kibana.alert.original_event.start", + "type": "alias" + }, + "timezone": { + "path": "kibana.alert.original_event.timezone", + "type": "alias" + }, + "type": { + "path": "kibana.alert.original_event.type", + "type": "alias" + } + } + }, + "original_time": { + "path": "kibana.alert.original_time", + "type": "alias" + }, + "reason": { + "path": "kibana.alert.reason", + "type": "alias" + }, + "rule": { + "properties": { + "author": { + "path": "kibana.alert.rule.author", + "type": "alias" + }, + "building_block_type": { + "path": "kibana.alert.building_block_type", + "type": "alias" + }, + "created_at": { + "path": "kibana.alert.rule.created_at", + "type": "alias" + }, + "created_by": { + "path": "kibana.alert.rule.created_by", + "type": "alias" + }, + "description": { + "path": "kibana.alert.rule.description", + "type": "alias" + }, + "enabled": { + "path": "kibana.alert.rule.enabled", + "type": "alias" + }, + "false_positives": { + "path": "kibana.alert.rule.false_positives", + "type": "alias" + }, + "from": { + "path": "kibana.alert.rule.from", + "type": "alias" + }, + "id": { + "path": "kibana.alert.rule.uuid", + "type": "alias" + }, + "immutable": { + "path": "kibana.alert.rule.immutable", + "type": "alias" + }, + "interval": { + "path": "kibana.alert.rule.interval", + "type": "alias" + }, + "license": { + "path": "kibana.alert.rule.license", + "type": "alias" + }, + "max_signals": { + "path": "kibana.alert.rule.max_signals", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.name", + "type": "alias" + }, + "note": { + "path": "kibana.alert.rule.note", + "type": "alias" + }, + "references": { + "path": "kibana.alert.rule.references", + "type": "alias" + }, + "risk_score": { + "path": "kibana.alert.risk_score", + "type": "alias" + }, + "rule_id": { + "path": "kibana.alert.rule.rule_id", + "type": "alias" + }, + "rule_name_override": { + "path": "kibana.alert.rule.rule_name_override", + "type": "alias" + }, + "severity": { + "path": "kibana.alert.severity", + "type": "alias" + }, + "tags": { + "path": "kibana.alert.rule.tags", + "type": "alias" + }, + "threat": { + "properties": { + "framework": { + "path": "kibana.alert.rule.threat.framework", + "type": "alias" + }, + "tactic": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.tactic.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.tactic.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.tactic.reference", + "type": "alias" + } + } + }, + "technique": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.technique.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.technique.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.technique.reference", + "type": "alias" + }, + "subtechnique": { + "properties": { + "id": { + "path": "kibana.alert.rule.threat.technique.subtechnique.id", + "type": "alias" + }, + "name": { + "path": "kibana.alert.rule.threat.technique.subtechnique.name", + "type": "alias" + }, + "reference": { + "path": "kibana.alert.rule.threat.technique.subtechnique.reference", + "type": "alias" + } + } + } + } + } + } + }, + "timeline_id": { + "path": "kibana.alert.rule.timeline_id", + "type": "alias" + }, + "timeline_title": { + "path": "kibana.alert.rule.timeline_title", + "type": "alias" + }, + "timestamp_override": { + "path": "kibana.alert.rule.timestamp_override", + "type": "alias" + }, + "to": { + "path": "kibana.alert.rule.to", + "type": "alias" + }, + "type": { + "path": "kibana.alert.rule.type", + "type": "alias" + }, + "updated_at": { + "path": "kibana.alert.rule.updated_at", + "type": "alias" + }, + "updated_by": { + "path": "kibana.alert.rule.updated_by", + "type": "alias" + }, + "version": { + "path": "kibana.alert.rule.version", + "type": "alias" + } + } + }, + "status": { + "path": "kibana.alert.workflow_status", + "type": "alias" + }, + "threshold_result": { + "properties": { + "cardinality": { + "properties": { + "field": { + "path": "kibana.alert.threshold_result.cardinality.field", + "type": "alias" + }, + "value": { + "path": "kibana.alert.threshold_result.cardinality.value", + "type": "alias" + } + } + }, + "count": { + "path": "kibana.alert.threshold_result.count", + "type": "alias" + }, + "from": { + "path": "kibana.alert.threshold_result.from", + "type": "alias" + }, + "terms": { + "properties": { + "field": { + "path": "kibana.alert.threshold_result.terms.field", + "type": "alias" + }, + "value": { + "path": "kibana.alert.threshold_result.terms.value", + "type": "alias" + } + } + } + } + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "span": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "enrichments": { + "properties": { + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "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": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlp_version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "matched": { + "properties": { + "atomic": { + "ignore_above": 1024, + "type": "keyword" + }, + "field": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "occurred": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "feed": { + "properties": { + "dashboard_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlsh": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "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": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pehash": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + }, + "tlp_version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "type": "wildcard" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "software": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platforms": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "type": "wildcard" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "type": "wildcard" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "changes": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "risk": { + "properties": { + "calculated_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "calculated_score": { + "type": "float" + }, + "calculated_score_norm": { + "type": "float" + }, + "static_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "static_score": { + "type": "float" + }, + "static_score_norm": { + "type": "float" + } + } + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "type": "match_only_text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "hidden": "true", + "mapping": { + "total_fields": { + "limit": "2500" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/test/functional/page_objects/asset_details.ts b/x-pack/test/functional/page_objects/asset_details.ts index d51341c0fef42..ef4e8967e6922 100644 --- a/x-pack/test/functional/page_objects/asset_details.ts +++ b/x-pack/test/functional/page_objects/asset_details.ts @@ -37,14 +37,9 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { }, async getAssetDetailsMetricsCharts() { - const container = await testSubjects.find('infraAssetDetailsMetricsChartGrid'); - return container.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricsChart"]'); - }, - - async getAssetDetailsNginxMetricsCharts() { - const container = await testSubjects.find('infraAssetDetailsNginxMetricsChartGrid'); + const container = await testSubjects.find('infraAssetDetailsHostMetricsChartGrid'); return container.findAllByCssSelector( - '[data-test-subj*="infraAssetDetailsNginxMetricsChart"]' + '[data-test-subj*="infraAssetDetailsHostMetricsChart"]' ); }, diff --git a/x-pack/test/functional/page_objects/remote_clusters_page.ts b/x-pack/test/functional/page_objects/remote_clusters_page.ts index b9f24dd1854d2..253ba3a27ff02 100644 --- a/x-pack/test/functional/page_objects/remote_clusters_page.ts +++ b/x-pack/test/functional/page_objects/remote_clusters_page.ts @@ -35,7 +35,7 @@ export function RemoteClustersPageProvider({ getService }: FtrProviderContext) { // Complete trust setup await testSubjects.click('setupTrustDoneButton'); - await testSubjects.setCheckbox('remoteClusterTrustCheckbox', 'check'); + await testSubjects.setCheckbox('remoteClusterTrustCheckboxLabel', 'check'); await testSubjects.click('remoteClusterTrustSubmitButton'); }, async getRemoteClustersList() { diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts index bb127df565ccd..b80219544ef58 100644 --- a/x-pack/test/functional/page_objects/security_page.ts +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -6,7 +6,7 @@ */ import { adminTestUser } from '@kbn/test'; -import { AuthenticatedUser, Role } from '@kbn/security-plugin/common/model'; +import { AuthenticatedUser, Role } from '@kbn/security-plugin/common'; import type { UserFormValues } from '@kbn/security-plugin/public/management/users/edit_user/user_form'; import { Key } from 'selenium-webdriver'; import { FtrService } from '../ftr_provider_context'; diff --git a/x-pack/test/functional/services/aiops/change_point_detection_page.ts b/x-pack/test/functional/services/aiops/change_point_detection_page.ts index 4f83137df472e..b4950763194c0 100644 --- a/x-pack/test/functional/services/aiops/change_point_detection_page.ts +++ b/x-pack/test/functional/services/aiops/change_point_detection_page.ts @@ -26,7 +26,7 @@ export function ChangePointDetectionPageProvider( const dashboardPage = getPageObject('dashboard'); return { - async navigateToIndexPatternSelection() { + async navigateToDataViewSelection() { await testSubjects.click('mlMainTab changePointDetection'); await testSubjects.existOrFail('mlPageSourceSelection'); }, diff --git a/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts index 59c786833a53e..661881a288381 100644 --- a/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts +++ b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts @@ -21,7 +21,7 @@ export function LogPatternAnalysisPageProvider({ getService, getPageObject }: Ft }); }, - async navigateToIndexPatternSelection() { + async navigateToDataViewSelection() { await testSubjects.click('mlMainTab logCategorization'); await testSubjects.existOrFail('mlPageSourceSelection'); }, diff --git a/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts b/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts index 48028b2ddbd1a..7e1ead80b4ff8 100644 --- a/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts +++ b/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts @@ -14,9 +14,15 @@ import { FtrProviderContext } from '../../ftr_provider_context'; const LOG_RATE_ANALYSYS_DATA_GENERATOR = { KIBANA_SAMPLE_DATA_LOGS: 'kibana_sample_data_logs', FAREQUOTE_WITH_SPIKE: 'farequote_with_spike', - ARTIFICIAL_LOGS_WITH_SPIKE_NOTEXTFIELD: 'artificial_logs_with_spike_notextfield', + ARTIFICIAL_LOGS_WITH_SPIKE_ZERODOCSFALLBACK: 'artificial_logs_with_spike_zerodocsfallback', + ARTIFICIAL_LOGS_WITH_SPIKE_TEXTFIELD_ZERODOCSFALLBACK: + 'artificial_logs_with_spike_textfield_zerodocsfallback', + ARTIFICIAL_LOGS_WITH_DIP_ZERODOCSFALLBACK: 'artificial_logs_with_dip_zerodocsfallback', + ARTIFICIAL_LOGS_WITH_DIP_TEXTFIELD_ZERODOCSFALLBACK: + 'artificial_logs_with_dip_textfield_zerodocsfallback', + ARTIFICIAL_LOGS_WITH_SPIKE: 'artificial_logs_with_spike', ARTIFICIAL_LOGS_WITH_SPIKE_TEXTFIELD: 'artificial_logs_with_spike_textfield', - ARTIFICIAL_LOGS_WITH_DIP_NOTEXTFIELD: 'artificial_logs_with_dip_notextfield', + ARTIFICIAL_LOGS_WITH_DIP: 'artificial_logs_with_dip', ARTIFICIAL_LOGS_WITH_DIP_TEXTFIELD: 'artificial_logs_with_dip_textfield', } as const; export type LogRateAnalysisDataGenerator = @@ -38,7 +44,7 @@ const DAY_MS = 86400000; const DEVIATION_TS = REFERENCE_TS - DAY_MS * 2; const BASELINE_TS = DEVIATION_TS - DAY_MS * 1; -function getMessage(timestamp: number, user: string, url: string, responseCode: string) { +function getTextFieldMessage(timestamp: number, user: string, url: string, responseCode: string) { const date = new Date(timestamp); return `${user} [${date.toLocaleString('en-US')}] "GET /${url} HTTP/1.1" ${responseCode}`; } @@ -46,12 +52,37 @@ function getMessage(timestamp: number, user: string, url: string, responseCode: function getArtificialLogsWithDeviation( index: string, deviationType: string, - includeTextField = false + includeTextField = false, + includeGaps = false ) { const bulkBody: estypes.BulkRequest['body'] = []; const action = { index: { _index: index } }; let tsOffset = 0; + if (includeGaps) { + const earliestDoc: GeneratedDoc = { + user: 'Peter', + response_code: '200', + url: 'login.php', + version: 'v1.0.0', + '@timestamp': BASELINE_TS - DAY_MS, + should_ignore_this_field: 'should_ignore_this_field', + }; + bulkBody.push(action); + bulkBody.push(earliestDoc); + + const latestDoc: GeneratedDoc = { + user: 'Peter', + response_code: '200', + url: 'login.php', + version: 'v1.0.0', + '@timestamp': DEVIATION_TS + 2 * DAY_MS, + should_ignore_this_field: 'should_ignore_this_field', + }; + bulkBody.push(action); + bulkBody.push(latestDoc); + } + // Creates docs evenly spread across baseline and deviation time frame [BASELINE_TS, DEVIATION_TS].forEach((ts) => { ['Peter', 'Paul', 'Mary'].forEach((user) => { @@ -66,8 +97,32 @@ function getArtificialLogsWithDeviation( ) ) { tsOffset = 0; - [...Array(100)].forEach(() => { - tsOffset += Math.round(DAY_MS / 100); + + let docCount = 100; + let responseCodeFactor = 1; + + if (includeGaps) { + if (responseCode === '404') { + responseCodeFactor = 2; + } else if (responseCode === '500') { + responseCodeFactor = 3; + } + + if (url === 'user.php') { + responseCodeFactor *= 2; + } else if (url === 'home.php') { + responseCodeFactor *= 3; + } + + if (user === 'Paul') { + docCount = 40 * responseCodeFactor; + } else if (user === 'Mary') { + docCount = 25 * responseCodeFactor; + } + } + + [...Array(docCount)].forEach(() => { + tsOffset += Math.round(DAY_MS / docCount); const timestamp = ts + tsOffset; const doc: GeneratedDoc = { user, @@ -79,7 +134,7 @@ function getArtificialLogsWithDeviation( }; if (includeTextField) { - doc.message = getMessage(timestamp, user, url, responseCode); + doc.message = getTextFieldMessage(timestamp, user, url, responseCode); } bulkBody.push(action); @@ -116,7 +171,7 @@ function getArtificialLogsWithDeviation( }; if (includeTextField) { - doc.message = getMessage(timestamp, 'Peter', url, responseCode); + doc.message = getTextFieldMessage(timestamp, 'Peter', url, responseCode); } bulkBody.push(action); @@ -204,10 +259,14 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider }); break; - case 'artificial_logs_with_spike_notextfield': + case 'artificial_logs_with_spike': case 'artificial_logs_with_spike_textfield': - case 'artificial_logs_with_dip_notextfield': + case 'artificial_logs_with_dip': case 'artificial_logs_with_dip_textfield': + case 'artificial_logs_with_spike_zerodocsfallback': + case 'artificial_logs_with_spike_textfield_zerodocsfallback': + case 'artificial_logs_with_dip_zerodocsfallback': + case 'artificial_logs_with_dip_textfield_zerodocsfallback': try { const indexExists = await es.indices.exists({ index: dataGenerator, @@ -238,12 +297,26 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider }); const dataGeneratorOptions = dataGenerator.split('_'); - const deviationType = dataGeneratorOptions[3] ?? LOG_RATE_ANALYSIS_TYPE.SPIKE; - const textField = dataGeneratorOptions[4] === 'textfield' ?? false; + + let deviationType = dataGeneratorOptions.includes(LOG_RATE_ANALYSIS_TYPE.SPIKE) + ? LOG_RATE_ANALYSIS_TYPE.SPIKE + : LOG_RATE_ANALYSIS_TYPE.DIP; + + const textField = dataGeneratorOptions.includes('textfield'); + const zeroDocsFallback = dataGeneratorOptions.includes('zerodocsfallback'); + + if (zeroDocsFallback) { + deviationType = LOG_RATE_ANALYSIS_TYPE.SPIKE; + } await es.bulk({ refresh: 'wait_for', - body: getArtificialLogsWithDeviation(dataGenerator, deviationType, textField), + body: getArtificialLogsWithDeviation( + dataGenerator, + deviationType, + textField, + zeroDocsFallback + ), }); break; @@ -262,10 +335,14 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); break; - case 'artificial_logs_with_spike_notextfield': + case 'artificial_logs_with_spike': case 'artificial_logs_with_spike_textfield': - case 'artificial_logs_with_dip_notextfield': + case 'artificial_logs_with_dip': case 'artificial_logs_with_dip_textfield': + case 'artificial_logs_with_spike_zerodocsfallback': + case 'artificial_logs_with_spike_textfield_zerodocsfallback': + case 'artificial_logs_with_dip_zerodocsfallback': + case 'artificial_logs_with_dip_textfield_zerodocsfallback': try { await es.indices.delete({ index: dataGenerator, diff --git a/x-pack/test/functional/services/aiops/log_rate_analysis_page.ts b/x-pack/test/functional/services/aiops/log_rate_analysis_page.ts index 510a056ca5c42..a8733fb2114a7 100644 --- a/x-pack/test/functional/services/aiops/log_rate_analysis_page.ts +++ b/x-pack/test/functional/services/aiops/log_rate_analysis_page.ts @@ -11,6 +11,8 @@ import type { LogRateAnalysisType } from '@kbn/aiops-utils'; import type { FtrProviderContext } from '../../ftr_provider_context'; +import type { LogRateAnalysisDataGenerator } from './log_rate_analysis_data_generator'; + export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrProviderContext) { const browser = getService('browser'); const elasticChart = getService('elasticChart'); @@ -241,7 +243,12 @@ export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrPr }); }, - async assertAnalysisComplete(analisysType: LogRateAnalysisType) { + async assertAnalysisComplete( + analysisType: LogRateAnalysisType, + dataGenerator: LogRateAnalysisDataGenerator + ) { + const dataGeneratorParts = dataGenerator.split('_'); + const zeroDocsFallback = dataGeneratorParts.includes('zerodocsfallback'); await retry.tryForTime(30 * 1000, async () => { await testSubjects.existOrFail('aiopsAnalysisComplete'); const currentProgressTitle = await testSubjects.getVisibleText('aiopsAnalysisComplete'); @@ -251,11 +258,22 @@ export function LogRateAnalysisPageProvider({ getService, getPageObject }: FtrPr const currentAnalysisTypeCalloutTitle = await testSubjects.getVisibleText( 'aiopsAnalysisTypeCalloutTitle' ); - expect(currentAnalysisTypeCalloutTitle).to.be(`Analysis type: Log rate ${analisysType}`); + + if (zeroDocsFallback && analysisType === 'spike') { + expect(currentAnalysisTypeCalloutTitle).to.be( + 'Analysis type: Top items for deviation time range' + ); + } else if (zeroDocsFallback && analysisType === 'dip') { + expect(currentAnalysisTypeCalloutTitle).to.be( + 'Analysis type: Top items for baseline time range' + ); + } else { + expect(currentAnalysisTypeCalloutTitle).to.be(`Analysis type: Log rate ${analysisType}`); + } }); }, - async navigateToIndexPatternSelection() { + async navigateToDataViewSelection() { await testSubjects.click('mlMainTab logRateAnalysis'); await testSubjects.existOrFail('mlPageSourceSelection'); }, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index beedbb145dce4..50d7738abf732 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -259,13 +259,13 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertDestIndexInputExists() { await retry.tryForTime(4000, async () => { - await testSubjects.existOrFail('mlAnalyticsCreateJobFlyoutDestinationIndexInput'); + await testSubjects.existOrFail('mlCreationWizardUtilsDestinationIndexInput'); }); }, async assertDestIndexValue(expectedValue: string) { const actualDestIndex = await testSubjects.getAttribute( - 'mlAnalyticsCreateJobFlyoutDestinationIndexInput', + 'mlCreationWizardUtilsDestinationIndexInput', 'value' ); expect(actualDestIndex).to.eql( @@ -275,13 +275,9 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async setDestIndex(destIndex: string) { - await mlCommonUI.setValueWithChecks( - 'mlAnalyticsCreateJobFlyoutDestinationIndexInput', - destIndex, - { - clearWithKeyboard: true, - } - ); + await mlCommonUI.setValueWithChecks('mlCreationWizardUtilsDestinationIndexInput', destIndex, { + clearWithKeyboard: true, + }); await this.assertDestIndexValue(destIndex); }, @@ -623,31 +619,40 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }); }, - async assertCreateIndexPatternSwitchExists() { - await testSubjects.existOrFail(`mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox`, { - allowHidden: true, - }); - }, - - async getCreateIndexPatternSwitchCheckState(): Promise { - const state = await testSubjects.getAttribute( - 'mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox', - 'checked' - ); - return state === 'true'; + async assertCreateDataViewSwitchExists() { + await testSubjects.existOrFail(`mlCreateDataViewSwitch`, { allowHidden: true }); }, - async assertCreateIndexPatternSwitchCheckState(expectedCheckState: boolean) { - const actualCheckState = await this.getCreateIndexPatternSwitchCheckState(); + async assertCreateDataViewSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute('mlCreateDataViewSwitch', 'aria-checked')) === 'true'; expect(actualCheckState).to.eql( expectedCheckState, `Create data view switch check state should be '${expectedCheckState}' (got '${actualCheckState}')` ); }, + async assertDataViewTimeFieldInputExists() { + await testSubjects.existOrFail(`mlDataViewTimeFieldSelect`); + }, + + async assertDataViewTimeFieldValue(expectedValue: string) { + const actualValue = await testSubjects.getAttribute(`mlDataViewTimeFieldSelect`, 'value'); + expect(actualValue).to.eql( + expectedValue, + `Data view time field should be ${expectedValue}, got ${actualValue}` + ); + }, + + async setDataViewTimeField(fieldName: string) { + const selectControl = await testSubjects.find('mlDataViewTimeFieldSelect'); + await selectControl.type(fieldName); + await this.assertDataViewTimeFieldValue(fieldName); + }, + async getDestIndexSameAsIdSwitchCheckState(): Promise { const state = await testSubjects.getAttribute( - 'mlAnalyticsCreateJobWizardDestIndexSameAsIdSwitch', + 'mlCreationWizardUtilsJobIdAsDestIndexNameSwitch', 'aria-checked' ); return state === 'true'; @@ -662,35 +667,32 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async assertDestIndexSameAsIdSwitchExists() { - await testSubjects.existOrFail(`mlAnalyticsCreateJobWizardDestIndexSameAsIdSwitch`, { + await testSubjects.existOrFail(`mlCreationWizardUtilsJobIdAsDestIndexNameSwitch`, { allowHidden: true, }); }, async setDestIndexSameAsIdCheckState(checkState: boolean) { if ((await this.getDestIndexSameAsIdSwitchCheckState()) !== checkState) { - await testSubjects.click('mlAnalyticsCreateJobWizardDestIndexSameAsIdSwitch'); + await testSubjects.click('mlCreationWizardUtilsJobIdAsDestIndexNameSwitch'); } await this.assertDestIndexSameAsIdCheckState(checkState); }, - async setCreateIndexPatternSwitchState(checkState: boolean) { - if ((await this.getCreateIndexPatternSwitchCheckState()) !== checkState) { - await testSubjects.click('mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox'); - } - await this.assertCreateIndexPatternSwitchCheckState(checkState); + async assertStartJobSwitchExists() { + await testSubjects.existOrFail('mlAnalyticsCreateJobWizardStartJobSwitch'); }, - async assertStartJobCheckboxExists() { - await testSubjects.existOrFail('mlAnalyticsCreateJobWizardStartJobCheckbox'); + async getStartJobSwitchCheckState(): Promise { + const state = await testSubjects.getAttribute( + 'mlAnalyticsCreateJobWizardStartJobSwitch', + 'aria-checked' + ); + return state === 'true'; }, - async assertStartJobCheckboxCheckState(expectedCheckState: boolean) { - const actualCheckState = - (await testSubjects.getAttribute( - 'mlAnalyticsCreateJobWizardStartJobCheckbox', - 'checked' - )) === 'true'; + async assertStartJobSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = await this.getStartJobSwitchCheckState(); expect(actualCheckState).to.eql( expectedCheckState, `Start job check state should be ${expectedCheckState} (got ${actualCheckState})` @@ -755,7 +757,6 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertCreationCalloutMessagesExist() { await testSubjects.existOrFail('analyticsWizardCreationCallout_0'); await testSubjects.existOrFail('analyticsWizardCreationCallout_1'); - await testSubjects.existOrFail('analyticsWizardCreationCallout_2'); }, async navigateToJobManagementPage() { diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts b/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts index a58820ae393bd..e05be8d8c768c 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts @@ -18,7 +18,7 @@ import { MlCustomUrls } from './custom_urls'; export interface DiscoverUrlConfig { label: string; - indexPattern: string; + indexName: string; queryEntityFieldNames: string[]; timeRange: TimeRangeType; timeRangeInterval?: string; @@ -98,7 +98,7 @@ export function MachineLearningDataFrameAnalyticsEditProvider( ); await mlCommonUI.selectSelectValueByVisibleText( 'mlJobCustomUrlDiscoverIndexPatternInput', - customUrl.indexPattern + customUrl.indexName ); await customUrls.setCustomUrlQueryEntityFieldNames(customUrl.queryEntityFieldNames); if (addTimerange) { diff --git a/x-pack/test/functional/services/ml/data_visualizer.ts b/x-pack/test/functional/services/ml/data_visualizer.ts index 3571f9193e6b3..cccdeaf07e4fc 100644 --- a/x-pack/test/functional/services/ml/data_visualizer.ts +++ b/x-pack/test/functional/services/ml/data_visualizer.ts @@ -55,7 +55,7 @@ export function MachineLearningDataVisualizerProvider({ getService }: FtrProvide ); }, - async navigateToIndexPatternSelection() { + async navigateToDataViewSelection() { await testSubjects.click('mlDataVisualizerSelectIndexButton'); await testSubjects.existOrFail('mlPageSourceSelection'); }, diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts index 22f43a08f1e78..5035b6844b9c3 100644 --- a/x-pack/test/functional/services/ml/test_resources.ts +++ b/x-pack/test/functional/services/ml/test_resources.ts @@ -120,7 +120,7 @@ export function MachineLearningTestResourcesProvider( return savedObjectIds; }, - async getIndexPatternId(title: string, space?: string): Promise { + async getDataViewId(title: string, space?: string): Promise { return this.getSavedObjectIdByTitle(title, SavedObjectType.INDEX_PATTERN, space); }, @@ -136,11 +136,7 @@ export function MachineLearningTestResourcesProvider( return this.getSavedObjectIdByTitle(title, SavedObjectType.DASHBOARD); }, - async createIndexPattern( - title: string, - timeFieldName?: string, - space?: string - ): Promise { + async createDataView(title: string, timeFieldName?: string, space?: string): Promise { log.debug( `Creating index pattern with title '${title}'${ timeFieldName !== undefined ? ` and time field '${timeFieldName}'` : '' @@ -153,7 +149,7 @@ export function MachineLearningTestResourcesProvider( .send({ attributes: { title, timeFieldName } }); mlApi.assertResponseStatusCode(200, status, createResponse); - await this.assertIndexPatternExistByTitle(title, space); + await this.assertDataViewExistByTitle(title, space); log.debug(` > Created with id '${createResponse.id}'`); return createResponse.id; @@ -172,21 +168,21 @@ export function MachineLearningTestResourcesProvider( return createResponse; }, - async createIndexPatternIfNeeded( + async createDataViewIfNeeded( title: string, timeFieldName?: string, space?: string ): Promise { - const indexPatternId = await this.getIndexPatternId(title, space); - if (indexPatternId !== undefined) { + const dataViewId = await this.getDataViewId(title, space); + if (dataViewId !== undefined) { log.debug(`Index pattern with title '${title}' already exists. Nothing to create.`); - return indexPatternId; + return dataViewId; } else { - return await this.createIndexPattern(title, timeFieldName, space); + return await this.createDataView(title, timeFieldName, space); } }, - async assertIndexPatternNotExist(title: string) { + async assertDataViewNotExist(title: string) { await this.assertSavedObjectNotExistsByTitle(title, SavedObjectType.INDEX_PATTERN); }, @@ -218,7 +214,7 @@ export function MachineLearningTestResourcesProvider( return createResponse.id; }, - async createSavedSearchIfNeeded(savedSearch: any, indexPatternTitle: string): Promise { + async createSavedSearchIfNeeded(savedSearch: any, dataViewTitle: string): Promise { const title = savedSearch.requestBody.attributes.title; const savedSearchId = await this.getSavedSearchId(title); if (savedSearchId !== undefined) { @@ -227,24 +223,24 @@ export function MachineLearningTestResourcesProvider( } else { const body = await this.updateSavedSearchRequestBody( savedSearch.requestBody, - indexPatternTitle + dataViewTitle ); return await this.createSavedSearch(title, body); } }, - async updateSavedSearchRequestBody(body: object, indexPatternTitle: string): Promise { - const indexPatternId = await this.getIndexPatternId(indexPatternTitle); - if (indexPatternId === undefined) { + async updateSavedSearchRequestBody(body: object, dataViewTitle: string): Promise { + const dataViewId = await this.getDataViewId(dataViewTitle); + if (dataViewId === undefined) { throw new Error( - `Index pattern '${indexPatternTitle}' to base saved search on does not exist. ` + `Index pattern '${dataViewTitle}' to base saved search on does not exist. ` ); } // inject index pattern id const updatedBody = JSON.parse(JSON.stringify(body), (_key, value) => { if (value === 'INDEX_PATTERN_ID_PLACEHOLDER') { - return indexPatternId; + return dataViewId; } else { return value; } @@ -258,8 +254,8 @@ export function MachineLearningTestResourcesProvider( return updatedBody; }, - async createSavedSearchFarequoteFilterIfNeeded(indexPatternTitle: string = 'ft_farequote') { - await this.createSavedSearchIfNeeded(savedSearches.farequoteFilter, indexPatternTitle); + async createSavedSearchFarequoteFilterIfNeeded(dataViewTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteFilter, dataViewTitle); }, async createMLTestDashboardIfNeeded(): Promise { @@ -281,48 +277,37 @@ export function MachineLearningTestResourcesProvider( } }, - async createSavedSearchFarequoteLuceneIfNeeded(indexPatternTitle: string = 'ft_farequote') { - await this.createSavedSearchIfNeeded(savedSearches.farequoteLucene, indexPatternTitle); + async createSavedSearchFarequoteLuceneIfNeeded(dataViewTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteLucene, dataViewTitle); }, - async createSavedSearchFarequoteKueryIfNeeded(indexPatternTitle: string = 'ft_farequote') { - await this.createSavedSearchIfNeeded(savedSearches.farequoteKuery, indexPatternTitle); + async createSavedSearchFarequoteKueryIfNeeded(dataViewTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteKuery, dataViewTitle); }, async createSavedSearchFarequoteFilterAndLuceneIfNeeded( - indexPatternTitle: string = 'ft_farequote' + dataViewTitle: string = 'ft_farequote' ) { - await this.createSavedSearchIfNeeded( - savedSearches.farequoteFilterAndLucene, - indexPatternTitle - ); + await this.createSavedSearchIfNeeded(savedSearches.farequoteFilterAndLucene, dataViewTitle); }, - async createSavedSearchFarequoteFilterAndKueryIfNeeded( - indexPatternTitle: string = 'ft_farequote' - ) { - await this.createSavedSearchIfNeeded( - savedSearches.farequoteFilterAndKuery, - indexPatternTitle - ); + async createSavedSearchFarequoteFilterAndKueryIfNeeded(dataViewTitle: string = 'ft_farequote') { + await this.createSavedSearchIfNeeded(savedSearches.farequoteFilterAndKuery, dataViewTitle); }, async createSavedSearchFarequoteFilterTwoAndLuceneIfNeeded( - indexPatternTitle: string = 'ft_farequote' + dataViewTitle: string = 'ft_farequote' ) { await this.createSavedSearchIfNeeded( savedSearches.farequoteFilterTwoAndLucene, - indexPatternTitle + dataViewTitle ); }, async createSavedSearchFarequoteFilterTwoAndKueryIfNeeded( - indexPatternTitle: string = 'ft_farequote' + dataViewTitle: string = 'ft_farequote' ) { - await this.createSavedSearchIfNeeded( - savedSearches.farequoteFilterTwoAndKuery, - indexPatternTitle - ); + await this.createSavedSearchIfNeeded(savedSearches.farequoteFilterTwoAndKuery, dataViewTitle); }, async deleteSavedObjectById( @@ -349,19 +334,19 @@ export function MachineLearningTestResourcesProvider( } }, - async deleteIndexPatternByTitle(title: string, space?: string) { + async deleteDataViewByTitle(title: string, space?: string) { log.debug(`Deleting index pattern with title '${title}'...`); - const indexPatternId = await this.getIndexPatternId(title, space); - if (indexPatternId === undefined) { + const dataViewId = await this.getDataViewId(title, space); + if (dataViewId === undefined) { log.debug(`Index pattern with title '${title}' does not exists. Nothing to delete.`); return; } else { - await this.deleteIndexPatternById(indexPatternId, space); + await this.deleteDataViewById(dataViewId, space); } }, - async deleteIndexPatternById(id: string, space?: string) { + async deleteDataViewById(id: string, space?: string) { await this.deleteSavedObjectById(id, SavedObjectType.INDEX_PATTERN, false, space); }, @@ -485,11 +470,11 @@ export function MachineLearningTestResourcesProvider( ); }, - async assertIndexPatternExistByTitle(title: string, space?: string) { + async assertDataViewExistByTitle(title: string, space?: string) { await this.assertSavedObjectExistsByTitle(title, SavedObjectType.INDEX_PATTERN, space); }, - async assertIndexPatternExistById(id: string) { + async assertDataViewExistById(id: string) { await this.assertSavedObjectExistsById(id, SavedObjectType.INDEX_PATTERN); }, diff --git a/x-pack/test/functional/services/observability/users.ts b/x-pack/test/functional/services/observability/users.ts index ba67ce8602f50..0e2915190d126 100644 --- a/x-pack/test/functional/services/observability/users.ts +++ b/x-pack/test/functional/services/observability/users.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Role } from '@kbn/security-plugin/common/model'; +import { Role } from '@kbn/security-plugin/common'; import { FtrProviderContext } from '../../ftr_provider_context'; type CreateRolePayload = Pick; diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 46b9bfadcdd02..5e00904ab8a27 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -700,13 +700,42 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await this.assertTransformDescriptionValue(transformDescription); }, + async getDestIndexSameAsIdSwitchCheckState(): Promise { + const state = await testSubjects.getAttribute( + 'mlCreationWizardUtilsJobIdAsDestIndexNameSwitch', + 'aria-checked' + ); + return state === 'true'; + }, + + async assertDestIndexSameAsIdCheckState(expectedCheckState: boolean) { + const actualCheckState = await this.getDestIndexSameAsIdSwitchCheckState(); + expect(actualCheckState).to.eql( + expectedCheckState, + `Destination index same as job id check state should be '${expectedCheckState}' (got '${actualCheckState}')` + ); + }, + + async assertDestIndexSameAsIdSwitchExists() { + await testSubjects.existOrFail(`mlCreationWizardUtilsJobIdAsDestIndexNameSwitch`, { + allowHidden: true, + }); + }, + + async setDestIndexSameAsIdCheckState(checkState: boolean) { + if ((await this.getDestIndexSameAsIdSwitchCheckState()) !== checkState) { + await testSubjects.click('mlCreationWizardUtilsJobIdAsDestIndexNameSwitch'); + } + await this.assertDestIndexSameAsIdCheckState(checkState); + }, + async assertDestinationIndexInputExists() { - await testSubjects.existOrFail('transformDestinationIndexInput'); + await testSubjects.existOrFail('mlCreationWizardUtilsDestinationIndexInput'); }, async assertDestinationIndexValue(expectedValue: string) { const actualDestinationIndex = await testSubjects.getAttribute( - 'transformDestinationIndexInput', + 'mlCreationWizardUtilsDestinationIndexInput', 'value' ); expect(actualDestinationIndex).to.eql( @@ -716,20 +745,23 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi }, async setDestinationIndex(destinationIndex: string) { - await ml.commonUI.setValueWithChecks('transformDestinationIndexInput', destinationIndex, { - clearWithKeyboard: true, - }); + await ml.commonUI.setValueWithChecks( + 'mlCreationWizardUtilsDestinationIndexInput', + destinationIndex, + { + clearWithKeyboard: true, + } + ); await this.assertDestinationIndexValue(destinationIndex); }, async assertCreateDataViewSwitchExists() { - await testSubjects.existOrFail(`transformCreateDataViewSwitch`, { allowHidden: true }); + await testSubjects.existOrFail(`mlCreateDataViewSwitch`, { allowHidden: true }); }, async assertCreateDataViewSwitchCheckState(expectedCheckState: boolean) { const actualCheckState = - (await testSubjects.getAttribute('transformCreateDataViewSwitch', 'aria-checked')) === - 'true'; + (await testSubjects.getAttribute('mlCreateDataViewSwitch', 'aria-checked')) === 'true'; expect(actualCheckState).to.eql( expectedCheckState, `Create data view switch check state should be '${expectedCheckState}' (got '${actualCheckState}')` @@ -737,14 +769,11 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi }, async assertDataViewTimeFieldInputExists() { - await testSubjects.existOrFail(`transformDataViewTimeFieldSelect`); + await testSubjects.existOrFail(`mlDataViewTimeFieldSelect`); }, async assertDataViewTimeFieldValue(expectedValue: string) { - const actualValue = await testSubjects.getAttribute( - `transformDataViewTimeFieldSelect`, - 'value' - ); + const actualValue = await testSubjects.getAttribute(`mlDataViewTimeFieldSelect`, 'value'); expect(actualValue).to.eql( expectedValue, `Data view time field should be ${expectedValue}, got ${actualValue}` @@ -752,7 +781,7 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi }, async setDataViewTimeField(fieldName: string) { - const selectControl = await testSubjects.find('transformDataViewTimeFieldSelect'); + const selectControl = await testSubjects.find('mlDataViewTimeFieldSelect'); await selectControl.type(fieldName); await this.assertDataViewTimeFieldValue(fieldName); }, diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/group1/index.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/group1/index.ts index 37295dda128ad..cf8f8ed7db84a 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/group1/index.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/group1/index.ts @@ -25,8 +25,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/group2/index.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/group2/index.ts index e1ed6d554a398..c4205e1bffd65 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/group2/index.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/group2/index.ts @@ -25,8 +25,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts index 18a5dfaec2d60..d2e77f9522854 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts @@ -25,8 +25,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index_data_visualizer_actions_panel.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index_data_visualizer_actions_panel.ts index f8e2c83a1afd7..62cbc2e41e36a 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index_data_visualizer_actions_panel.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index_data_visualizer_actions_panel.ts @@ -20,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await ml.testResources.createIndexPatternIfNeeded(indexPatternName, '@timestamp'); + await ml.testResources.createDataViewIfNeeded(indexPatternName, '@timestamp'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); await ml.testResources.setKibanaTimeZoneToUTC(); @@ -34,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToDataVisualizer(); await ml.testExecution.logTestStep('loads the saved search selection page'); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.testExecution.logTestStep('loads the index data visualizer page'); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(savedSearch); diff --git a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts index 0ff4eca82a5cf..49b477f9e110d 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/full_ml_access.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/module_sample_ecommerce' ); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.createDataViewIfNeeded(ecIndexPattern, 'order_date'); await ml.securityUI.loginAs(testUser.user); }); @@ -105,7 +105,7 @@ export default function ({ getService }: FtrProviderContext) { it('should display elements on Index Data Visualizer page correctly', async () => { await ml.testExecution.logTestStep('should load an index into the data visualizer page'); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); await ml.testExecution.logTestStep('should display the time range step'); diff --git a/x-pack/test/functional_basic/apps/ml/permissions/index.ts b/x-pack/test/functional_basic/apps/ml/permissions/index.ts index 53e78b4d08c15..8e72bb7bf51e2 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/index.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/index.ts @@ -25,8 +25,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.deleteSavedSearches(); - await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); - await ml.testResources.deleteIndexPatternByTitle('ft_module_sample_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/farequote'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/module_sample_ecommerce'); diff --git a/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts index c7a3f13d23ed7..60554e8b1604e 100644 --- a/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional_basic/apps/ml/permissions/read_ml_access.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.loadIfNeeded( 'x-pack/test/functional/es_archives/ml/module_sample_ecommerce' ); - await ml.testResources.createIndexPatternIfNeeded(ecIndexPattern, 'order_date'); + await ml.testResources.createDataViewIfNeeded(ecIndexPattern, 'order_date'); await ml.securityUI.loginAs(testUser.user); }); @@ -105,7 +105,7 @@ export default function ({ getService }: FtrProviderContext) { it('should display elements on Index Data Visualizer page correctly', async () => { await ml.testExecution.logTestStep('should load an index into the data visualizer page'); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(ecIndexPattern); await ml.testExecution.logTestStep('should display the time range step'); diff --git a/x-pack/test/functional_cloud/tests/cloud_links.ts b/x-pack/test/functional_cloud/tests/cloud_links.ts index 94f67401c98fd..6125b52135b9c 100644 --- a/x-pack/test/functional_cloud/tests/cloud_links.ts +++ b/x-pack/test/functional_cloud/tests/cloud_links.ts @@ -48,11 +48,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('A button to open a modal to view the CloudID and ES endpoint is added', async () => { - await PageObjects.common.clickAndValidate('helpMenuButton', 'endpointsHelpLink'); - expect(await find.byCssSelector('[data-test-subj="endpointsHelpLink"]')).to.not.be(null); + await PageObjects.common.clickAndValidate('helpMenuButton', 'connectionDetailsHelpLink'); + expect(await find.byCssSelector('[data-test-subj="connectionDetailsHelpLink"]')).to.not.be( + null + ); // Open the modal - await PageObjects.common.clickAndValidate('endpointsHelpLink', 'deploymentDetailsModal'); + await PageObjects.common.clickAndValidate( + 'connectionDetailsHelpLink', + 'deploymentDetailsModal' + ); const esEndpointInput = await find.byCssSelector( '[data-test-subj="deploymentDetailsEsEndpoint"]' diff --git a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/alert_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/alert_flyout.ts index de7a2e42fda01..037e33267dfdb 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/alert_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/alert_flyout.ts @@ -70,7 +70,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('anomaly detection alert', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); diff --git a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/index.ts b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/index.ts index ef104c74b820d..7a898b63c3f22 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/index.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/ml/index.ts @@ -23,7 +23,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { // NOTE: Logout needs to happen before anything else to avoid flaky behavior await ml.securityUI.logout(); - await ml.testResources.deleteIndexPatternByTitle('ft_ecommerce'); + await ml.testResources.deleteDataViewByTitle('ft_ecommerce'); await esArchiver.unload('x-pack/test/functional/es_archives/ml/ecommerce'); await ml.securityCommon.cleanMlUsers(); await ml.securityCommon.cleanMlRoles(); diff --git a/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx b/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx index 851134d346d9a..af4fb983124e8 100644 --- a/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx +++ b/x-pack/test/functional_with_es_ssl/plugins/cases/public/application.tsx @@ -43,6 +43,7 @@ const permissions = { delete: true, push: true, connectors: true, + settings: true, }; const attachments = [{ type: AttachmentType.user as const, comment: 'test' }]; diff --git a/x-pack/test/lists_api_integration/common/config.ts b/x-pack/test/lists_api_integration/common/config.ts deleted file mode 100644 index 62663ae3915b0..0000000000000 --- a/x-pack/test/lists_api_integration/common/config.ts +++ /dev/null @@ -1,66 +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 { CA_CERT_PATH } from '@kbn/dev-utils'; -import { FtrConfigProviderContext } from '@kbn/test'; -import { services } from './services'; - -interface CreateTestConfigOptions { - license: string; - disabledPlugins?: string[]; - ssl?: boolean; -} - -export function createTestConfig(name: string, options: CreateTestConfigOptions) { - const { license = 'trial', disabledPlugins = [], ssl = false } = options; - - return async ({ readConfigFile }: FtrConfigProviderContext) => { - const xPackApiIntegrationTestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.ts') - ); - const servers = { - ...xPackApiIntegrationTestsConfig.get('servers'), - elasticsearch: { - ...xPackApiIntegrationTestsConfig.get('servers.elasticsearch'), - protocol: ssl ? 'https' : 'http', - }, - }; - - return { - testFiles: [require.resolve(`../${name}/tests/`)], - servers, - services, - junit: { - reportName: 'X-Pack Lists Integration Tests', - }, - esTestCluster: { - ...xPackApiIntegrationTestsConfig.get('esTestCluster'), - license, - ssl, - serverArgs: [ - `xpack.license.self_generated.type=${license}`, - `xpack.security.enabled=${!disabledPlugins.includes('security')}`, - ], - }, - kbnTestServer: { - ...xPackApiIntegrationTestsConfig.get('kbnTestServer'), - serverArgs: [ - ...xPackApiIntegrationTestsConfig.get('kbnTestServer.serverArgs'), - ...disabledPlugins - .filter((k) => k !== 'security') - .map((key) => `--xpack.${key}.enabled=false`), - ...(ssl - ? [ - `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, - `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, - ] - : []), - ], - }, - }; - }; -} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts deleted file mode 100644 index 79217043b36bb..0000000000000 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ loadTestFile }: FtrProviderContext): void => { - describe('lists api security and spaces enabled', function () { - loadTestFile(require.resolve('./create_lists')); - loadTestFile(require.resolve('./create_lists_index')); - loadTestFile(require.resolve('./create_list_items')); - loadTestFile(require.resolve('./patch_lists')); - loadTestFile(require.resolve('./patch_list_items')); - loadTestFile(require.resolve('./read_lists')); - loadTestFile(require.resolve('./read_list_items')); - loadTestFile(require.resolve('./update_lists')); - loadTestFile(require.resolve('./update_list_items')); - loadTestFile(require.resolve('./delete_lists')); - loadTestFile(require.resolve('./delete_list_items')); - loadTestFile(require.resolve('./duplicate_exception_list')); - loadTestFile(require.resolve('./find_lists')); - loadTestFile(require.resolve('./find_list_items')); - loadTestFile(require.resolve('./find_lists_by_size')); - loadTestFile(require.resolve('./get_exception_filter')); - loadTestFile(require.resolve('./import_exceptions')); - loadTestFile(require.resolve('./import_list_items')); - loadTestFile(require.resolve('./export_list_items')); - loadTestFile(require.resolve('./export_exception_list')); - loadTestFile(require.resolve('./create_exception_lists')); - loadTestFile(require.resolve('./create_exception_list_items')); - loadTestFile(require.resolve('./read_exception_lists')); - loadTestFile(require.resolve('./read_exception_list_items')); - loadTestFile(require.resolve('./update_exception_lists')); - loadTestFile(require.resolve('./update_exception_list_items')); - loadTestFile(require.resolve('./delete_exception_lists')); - loadTestFile(require.resolve('./delete_exception_list_items')); - loadTestFile(require.resolve('./find_exception_lists')); - loadTestFile(require.resolve('./find_exception_list_items')); - loadTestFile(require.resolve('./read_list_privileges')); - loadTestFile(require.resolve('./summary_exception_lists')); - }); -}; diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index 7e48895a9060f..2217310e4e6ae 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -145,6 +145,7 @@ export default function ({ getService }: FtrProviderContext) { 'security:endpoint-meta-telemetry', 'security:telemetry-configuration', 'security:telemetry-detection-rules', + 'security:telemetry-diagnostic-timelines', 'security:telemetry-filterlist-artifact', 'security:telemetry-lists', 'security:telemetry-prebuilt-rule-alerts', diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts index 94cbdbce77491..fcf67163b2bbb 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/get_browser_fields_by_feature_id.ts @@ -45,9 +45,14 @@ export default ({ getService }: FtrProviderContext) => { 'logs', 'uptime', ]); - expect(Object.keys(resp.browserFields)).toEqual( - expect.arrayContaining(['base', 'event', 'kibana']) - ); + expect(Object.keys(resp.browserFields)).toEqual([ + 'base', + 'cloud', + 'container', + 'host', + 'kibana', + 'orchestrator', + ]); }); it(`${superUser.username} should be able to get browser fields for o11y featureIds`, async () => { @@ -57,21 +62,14 @@ export default ({ getService }: FtrProviderContext) => { 'logs', 'uptime', ]); - expect(Object.keys(resp.browserFields)).toEqual( - expect.arrayContaining([ - 'base', - 'agent', - 'anomaly', - 'ecs', - 'error', - 'event', - 'kibana', - 'monitor', - 'observer', - 'tls', - 'url', - ]) - ); + expect(Object.keys(resp.browserFields)).toEqual([ + 'base', + 'cloud', + 'container', + 'host', + 'kibana', + 'orchestrator', + ]); }); it(`${superUser.username} should NOT be able to get browser fields for siem featureId`, async () => { diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap index a66368fe5a8ec..bc4b903dee43e 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/__snapshots__/create_rule.snap @@ -26,6 +26,9 @@ Object { "kibana.alert.reason": Array [ "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", ], + "kibana.alert.reason.text": Array [ + "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", + ], "kibana.alert.rule.category": Array [ "Failed transaction rate threshold", ], @@ -109,6 +112,9 @@ Object { "kibana.alert.reason": Array [ "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", ], + "kibana.alert.reason.text": Array [ + "Failed transactions is 50% in the last 5 mins for service: opbeans-go, env: Not defined, type: request. Alert when > 30%.", + ], "kibana.alert.rule.category": Array [ "Failed transaction rate threshold", ], diff --git a/x-pack/test/scalability/apis/api.telemetry.cluster_stats.1600_dataviews.json b/x-pack/test/scalability/apis/api.telemetry.cluster_stats.1600_dataviews.json index 485208916d48e..de436beed4601 100644 --- a/x-pack/test/scalability/apis/api.telemetry.cluster_stats.1600_dataviews.json +++ b/x-pack/test/scalability/apis/api.telemetry.cluster_stats.1600_dataviews.json @@ -20,9 +20,7 @@ }, "testData": { "esArchives": [], - "kbnArchives": [ - "x-pack/test/scalability/fixtures/kbn_archiver/1600-dataviews.json" - ] + "kbnArchives": ["x-pack/test/scalability/fixtures/kbn_archiver/1600-dataviews.json"] }, "streams": [ { diff --git a/x-pack/test/scalability/apis/api.telemetry.cluster_stats.no_cache.1600_dataviews.json b/x-pack/test/scalability/apis/api.telemetry.cluster_stats.no_cache.1600_dataviews.json index 2a3095447e8b4..b9a690027c57a 100644 --- a/x-pack/test/scalability/apis/api.telemetry.cluster_stats.no_cache.1600_dataviews.json +++ b/x-pack/test/scalability/apis/api.telemetry.cluster_stats.no_cache.1600_dataviews.json @@ -25,9 +25,7 @@ }, "testData": { "esArchives": [], - "kbnArchives": [ - "x-pack/test/scalability/fixtures/kbn_archiver/1600-dataviews.json" - ] + "kbnArchives": ["x-pack/test/scalability/fixtures/kbn_archiver/1600-dataviews.json"] }, "streams": [ { diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts index cec6ed8528a10..8d93f1349cae6 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts @@ -78,7 +78,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('anomaly detection alert', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce'); - await ml.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); + await ml.testResources.createDataViewIfNeeded('ft_ecommerce', 'order_date'); const { job, datafeed } = createTestJobAndDatafeed(); diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/geographic_data.ts b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/geographic_data.ts index e79539d01bbb6..c6db2071f2354 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/geographic_data.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/geographic_data.ts @@ -101,7 +101,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.testExecution.logTestStep('open index in data visualizer'); await ml.navigation.navigateToMl(); await ml.navigation.navigateToDataVisualizer(); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(LOGS_INDEX_PATTERN); await ml.testExecution.logTestStep('set data visualizer options'); diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/mapping_anomalies.ts b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/mapping_anomalies.ts index c6affe8bf3c3a..7dcc26758ed46 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/mapping_anomalies.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/mapping_anomalies.ts @@ -60,7 +60,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await ml.testExecution.logTestStep('open index in data visualizer'); await ml.navigation.navigateToMl(); await ml.navigation.navigateToDataVisualizer(); - await ml.dataVisualizer.navigateToIndexPatternSelection(); + await ml.dataVisualizer.navigateToDataViewSelection(); await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(LOGS_INDEX_PATTERN); await ml.testExecution.logTestStep('set data visualizer options'); diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts index 3dc766e428b59..f49750a993d07 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/classification.ts @@ -40,12 +40,12 @@ export default function ({ getService }: FtrProviderContext) { describe('classification job', function () { before(async () => { await ml.api.createAndRunDFAJob(classificationJobConfig as DataFrameAnalyticsConfig); - await ml.testResources.createIndexPatternIfNeeded(classificationJobConfig.dest!.index!); + await ml.testResources.createDataViewIfNeeded(classificationJobConfig.dest!.index!); }); after(async () => { await ml.api.deleteDataFrameAnalyticsJobES(classificationJobConfig.id as string); - await ml.testResources.deleteIndexPatternByTitle(classificationJobConfig.dest!.index!); + await ml.testResources.deleteDataViewByTitle(classificationJobConfig.dest!.index!); await ml.api.deleteIndices(classificationJobConfig.dest!.index!); await ml.api.cleanMlIndices(); }); diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/outlier_detection.ts b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/outlier_detection.ts index 73e3a22d28c94..88e6091cd008b 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/outlier_detection.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/outlier_detection.ts @@ -49,19 +49,19 @@ export default function ({ getService }: FtrProviderContext) { describe('outlier detection job', function () { before(async () => { await transform.api.createAndRunTransform(transformConfig.id, transformConfig); - await ml.testResources.createIndexPatternIfNeeded(transformConfig.dest.index); + await ml.testResources.createDataViewIfNeeded(transformConfig.dest.index); await ml.api.createAndRunDFAJob(outlierJobConfig as DataFrameAnalyticsConfig); - await ml.testResources.createIndexPatternIfNeeded(outlierJobConfig.dest!.index!); + await ml.testResources.createDataViewIfNeeded(outlierJobConfig.dest!.index!); }); after(async () => { - await ml.testResources.deleteIndexPatternByTitle(transformConfig.dest.index); + await ml.testResources.deleteDataViewByTitle(transformConfig.dest.index); await transform.api.deleteIndices(transformConfig.dest.index); await transform.api.cleanTransformIndices(); await ml.api.deleteDataFrameAnalyticsJobES(outlierJobConfig.id as string); - await ml.testResources.deleteIndexPatternByTitle(outlierJobConfig.dest!.index!); + await ml.testResources.deleteDataViewByTitle(outlierJobConfig.dest!.index!); await ml.api.deleteIndices(outlierJobConfig.dest!.index!); await ml.api.cleanMlIndices(); }); diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/regression.ts b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/regression.ts index edf8c8d971a6c..3dc79e031a8b4 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/regression.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/data_frame_analytics/regression.ts @@ -41,12 +41,12 @@ export default function ({ getService }: FtrProviderContext) { describe('regression job', function () { before(async () => { await ml.api.createAndRunDFAJob(regressionJobConfig as DataFrameAnalyticsConfig); - await ml.testResources.createIndexPatternIfNeeded(regressionJobConfig.dest!.index!); + await ml.testResources.createDataViewIfNeeded(regressionJobConfig.dest!.index!); }); after(async () => { await ml.api.deleteDataFrameAnalyticsJobES(regressionJobConfig.id as string); - await ml.testResources.deleteIndexPatternByTitle(regressionJobConfig.dest!.index!); + await ml.testResources.deleteDataViewByTitle(regressionJobConfig.dest!.index!); await ml.api.deleteIndices(regressionJobConfig.dest!.index!); await ml.api.cleanMlIndices(); }); diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index_threshold_rule.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index_threshold_rule.ts index 9ca11a5a2dc45..582bc02bd5609 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index_threshold_rule.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/index_threshold_rule.ts @@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'rule-types-index-threshold-conditions', screenshotDirectories, 1400, - 1024 + 1300 ); await testSubjects.scrollIntoView('selectIndexExpression'); @@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'rule-types-index-threshold-example-aggregation', screenshotDirectories, 1400, - 1024 + 1300 ); await ofComboBox.type('bytes'); const ofOptionsString = await comboBox.getOptionsList('availablefieldsOptionsComboBox'); @@ -103,7 +103,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'rule-types-index-threshold-example-threshold', screenshotDirectories, 1400, - 1024 + 1300 ); await testSubjects.setValue('intervalInput', '4'); diff --git a/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts b/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts index 567b5e5317f7a..1f2dc1ab43775 100644 --- a/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts +++ b/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts @@ -10,7 +10,7 @@ import { readFileSync } from 'fs'; import url from 'url'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import expect from '@kbn/expect'; -import type { AuthenticationProvider } from '@kbn/security-plugin/common/model'; +import type { AuthenticationProvider } from '@kbn/security-plugin/common'; import { getStateAndNonce } from '@kbn/security-api-integration-helpers/oidc/oidc_tools'; import { getMutualAuthenticationResponseToken, diff --git a/x-pack/test/security_api_integration/tests/session_concurrent_limit/global_limit.ts b/x-pack/test/security_api_integration/tests/session_concurrent_limit/global_limit.ts index 56e1f91a1ef19..b0d65147b054a 100644 --- a/x-pack/test/security_api_integration/tests/session_concurrent_limit/global_limit.ts +++ b/x-pack/test/security_api_integration/tests/session_concurrent_limit/global_limit.ts @@ -8,7 +8,7 @@ import { parse as parseCookie, Cookie } from 'tough-cookie'; import expect from '@kbn/expect'; import { adminTestUser } from '@kbn/test'; -import type { AuthenticationProvider } from '@kbn/security-plugin/common/model'; +import type { AuthenticationProvider } from '@kbn/security-plugin/common'; import { getSAMLRequestId, getSAMLResponse, diff --git a/x-pack/test/security_api_integration/tests/session_invalidate/invalidate.ts b/x-pack/test/security_api_integration/tests/session_invalidate/invalidate.ts index 0f79624c830e3..b97808d535f79 100644 --- a/x-pack/test/security_api_integration/tests/session_invalidate/invalidate.ts +++ b/x-pack/test/security_api_integration/tests/session_invalidate/invalidate.ts @@ -8,7 +8,7 @@ import { parse as parseCookie, Cookie } from 'tough-cookie'; import expect from '@kbn/expect'; import { adminTestUser } from '@kbn/test'; -import type { AuthenticationProvider } from '@kbn/security-plugin/common/model'; +import type { AuthenticationProvider } from '@kbn/security-plugin/common'; import { getSAMLRequestId, getSAMLResponse, diff --git a/x-pack/test/security_api_integration/tests/session_lifespan/cleanup.ts b/x-pack/test/security_api_integration/tests/session_lifespan/cleanup.ts index 09e5e7998750d..8186cdbded722 100644 --- a/x-pack/test/security_api_integration/tests/session_lifespan/cleanup.ts +++ b/x-pack/test/security_api_integration/tests/session_lifespan/cleanup.ts @@ -9,7 +9,7 @@ import { parse as parseCookie, Cookie } from 'tough-cookie'; import { setTimeout as setTimeoutAsync } from 'timers/promises'; import expect from '@kbn/expect'; import { adminTestUser } from '@kbn/test'; -import type { AuthenticationProvider } from '@kbn/security-plugin/common/model'; +import type { AuthenticationProvider } from '@kbn/security-plugin/common'; import { getSAMLRequestId, getSAMLResponse, diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts new file mode 100644 index 0000000000000..15104fe97292a --- /dev/null +++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from './config.base'; + +export default createTestConfig({ + license: 'basic', + ssl: true, +}); diff --git a/x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.ts b/x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.ts new file mode 100644 index 0000000000000..377d970d3451d --- /dev/null +++ b/x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.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 { FtrConfigProviderContext } from '@kbn/test'; +export interface CreateTestConfigOptions { + testFiles: string[]; + junit: { reportName: string }; + kbnTestServerArgs?: string[]; + kbnTestServerEnv?: Record; +} +import { services } from '../../../../test_serverless/api_integration/services'; + +export function createTestConfig(options: CreateTestConfigOptions) { + return async ({ readConfigFile }: FtrConfigProviderContext) => { + const svlSharedConfig = await readConfigFile( + require.resolve('../../../../test_serverless/shared/config.base.ts') + ); + return { + ...svlSharedConfig.getAll(), + services: { + ...services, + }, + kbnTestServer: { + ...svlSharedConfig.get('kbnTestServer'), + serverArgs: [ + ...svlSharedConfig.get('kbnTestServer.serverArgs'), + '--serverless=security', + `--xpack.securitySolutionServerless.productTypes=${JSON.stringify([ + { product_line: 'security', product_tier: 'essentials' }, + { product_line: 'endpoint', product_tier: 'essentials' }, + ])}`, + ...(options.kbnTestServerArgs || []), + ], + env: { + ...svlSharedConfig.get('kbnTestServer.env'), + ...options.kbnTestServerEnv, + }, + }, + testFiles: options.testFiles, + junit: options.junit, + + mochaOpts: { + ...svlSharedConfig.get('mochaOpts'), + grep: '/^(?!.*@brokenInServerless).*@serverless.*/', + }, + }; + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/ftr_provider_context_with_spaces.d.ts b/x-pack/test/security_solution_api_integration/ftr_provider_context_with_spaces.d.ts similarity index 71% rename from x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/ftr_provider_context_with_spaces.d.ts rename to x-pack/test/security_solution_api_integration/ftr_provider_context_with_spaces.d.ts index 922a5d9d25b71..f6f4e00f0b4a3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/ftr_provider_context_with_spaces.d.ts +++ b/x-pack/test/security_solution_api_integration/ftr_provider_context_with_spaces.d.ts @@ -6,8 +6,8 @@ */ import { GenericFtrProviderContext } from '@kbn/test'; -import { SpacesServiceProvider } from '../../../../../../common/services/spaces'; -import { services as serverlessServices } from '../../../../../../../test_serverless/api_integration/services'; +import { SpacesServiceProvider } from '../common/services/spaces'; +import { services as serverlessServices } from '../../test_serverless/api_integration/services'; const services = { ...serverlessServices, diff --git a/x-pack/test/security_solution_api_integration/package.json b/x-pack/test/security_solution_api_integration/package.json index 50dc47f95b1b3..05299d0db6b96 100644 --- a/x-pack/test/security_solution_api_integration/package.json +++ b/x-pack/test/security_solution_api_integration/package.json @@ -7,8 +7,12 @@ "scripts": { "initialize-server:dr:default": "node ./scripts/index.js server detections_response default_license", "run-tests:dr:default": "node ./scripts/index.js runner detections_response default_license", + "initialize-server:dr:basicEssentials": "node ./scripts/index.js server detections_response basic_essentials_license", + "run-tests:dr:basicEssentials": "node ./scripts/index.js runner detections_response basic_essentials_license", "initialize-server:ea:default": "node ./scripts/index.js server entity_analytics default_license", "run-tests:ea:default": "node ./scripts/index.js runner entity_analytics default_license", + "initialize-server:lists:default": "node ./scripts/index.js server lists_and_exception_lists default_license", + "run-tests:lists:default": "node ./scripts/index.js runner lists_and_exception_lists default_license", "exception_workflows:server:serverless": "npm run initialize-server:dr:default exceptions/workflows serverless", "exception_workflows:runner:serverless": "npm run run-tests:dr:default exceptions/workflows serverless serverlessEnv", "exception_workflows:qa:serverless": "npm run run-tests:dr:default exceptions/workflows serverless qaEnv", @@ -98,6 +102,21 @@ "telemetry:runner:serverless": "npm run run-tests:dr:default telemetry serverless serverlessEnv", "telemetry:qa:serverless": "npm run run-tests:dr:default telemetry serverless qaEnv", "telemetry:server:ess": "npm run initialize-server:dr:default telemetry ess", - "telemetry:runner:ess": "npm run run-tests:dr:default telemetry ess essEnv" + "telemetry:runner:ess": "npm run run-tests:dr:default telemetry ess essEnv", + "detection_engine_basicessentionals:server:serverless": "npm run initialize-server:dr:basicEssentials detection_engine serverless", + "detection_engine_basicessentionals:runner:serverless": "npm run run-tests:dr:basicEssentials detection_engine serverless serverlessEnv", + "detection_engine_basicessentionals:qa:serverless": "npm run run-tests:dr:basicEssentials detection_engine serverless qaEnv", + "detection_engine_basicessentionals:server:ess": "npm run initialize-server:dr:basicEssentials detection_engine ess", + "detection_engine_basicessentionals:runner:ess": "npm run run-tests:dr:basicEssentials detection_engine ess essEnv", + "exception_lists_items:server:serverless": "npm run initialize-server:lists:default exception_lists_items serverless", + "exception_lists_items:runner:serverless": "npm run run-tests:lists:default exception_lists_items serverless serverlessEnv", + "exception_lists_items:qa:serverless": "npm run run-tests:lists:default exception_lists_items serverless qaEnv", + "exception_lists_items:server:ess": "npm run initialize-server:lists:default exception_lists_items ess", + "exception_lists_items:runner:ess": "npm run run-tests:lists:default exception_lists_items ess essEnv", + "lists_items:server:serverless": "npm run initialize-server:lists:default lists_items serverless", + "lists_items:runner:serverless": "npm run run-tests:lists:default lists_items serverless serverlessEnv", + "lists_items:qa:serverless": "npm run run-tests:lists:default lists_items serverless qaEnv", + "lists_items:server:ess": "npm run initialize-server:lists:default lists_items ess", + "lists_items:runner:ess": "npm run run-tests:lists:default lists_items ess essEnv" } } diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/open_close_alerts.ts similarity index 52% rename from x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/open_close_alerts.ts index cc1216e80dd8f..4af66d1da4a93 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/open_close_alerts.ts @@ -14,40 +14,45 @@ import { DETECTION_ENGINE_QUERY_SIGNALS_URL, } from '@kbn/security-solution-plugin/common/constants'; import { DetectionAlert } from '@kbn/security-solution-plugin/common/api/detection_engine'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { - createSignalsIndex, - setSignalStatus, - getQuerySignalIds, + createAlertsIndex, + setAlertStatus, + getQueryAlertIds, deleteAllRules, createRule, - waitForSignalsToBePresent, - getSignalsByIds, + waitForAlertsToBePresent, + getAlertsByIds, waitForRuleSuccess, - getRuleForSignalTesting, + getRuleForAlertTesting, deleteAllAlerts, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const log = getService('log'); const es = getService('es'); + // TODO: add a new service + const config = getService('config'); + const isServerless = config.get('serverless'); + const dataPathBuilder = new EsArchivePathBuilder(isServerless); + const auditbeatHost = dataPathBuilder.getPath('auditbeat/hosts'); - describe('open_close_signals', () => { + describe('@ess @serverless open_close_alerts', () => { describe('tests with auditbeat data', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load(auditbeatHost); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload(auditbeatHost); }); beforeEach(async () => { await deleteAllRules(supertest, log); - await createSignalsIndex(supertest, log); + await createAlertsIndex(supertest, log); }); afterEach(async () => { @@ -55,94 +60,94 @@ export default ({ getService }: FtrProviderContext) => { await deleteAllRules(supertest, log); }); - it('should be able to execute and get 10 signals', async () => { + it('should be able to execute and get 10 alerts', async () => { const rule = { - ...getRuleForSignalTesting(['auditbeat-*']), + ...getRuleForAlertTesting(['auditbeat-*']), query: 'process.executable: "/usr/bin/sudo"', }; const { id } = await createRule(supertest, log, rule); await waitForRuleSuccess({ supertest, log, id }); - await waitForSignalsToBePresent(supertest, log, 10, [id]); - const signalsOpen = await getSignalsByIds(supertest, log, [id]); - expect(signalsOpen.hits.hits.length).equal(10); + await waitForAlertsToBePresent(supertest, log, 10, [id]); + const alertsOpen = await getAlertsByIds(supertest, log, [id]); + expect(alertsOpen.hits.hits.length).equal(10); }); - it('should be have set the signals in an open state initially', async () => { + it('should be have set the alerts in an open state initially', async () => { const rule = { - ...getRuleForSignalTesting(['auditbeat-*']), + ...getRuleForAlertTesting(['auditbeat-*']), query: 'process.executable: "/usr/bin/sudo"', }; const { id } = await createRule(supertest, log, rule); await waitForRuleSuccess({ supertest, log, id }); - await waitForSignalsToBePresent(supertest, log, 10, [id]); - const signalsOpen = await getSignalsByIds(supertest, log, [id]); - const everySignalOpen = signalsOpen.hits.hits.every( + await waitForAlertsToBePresent(supertest, log, 10, [id]); + const alertsOpen = await getAlertsByIds(supertest, log, [id]); + const everyAlertOpen = alertsOpen.hits.hits.every( (hit) => hit._source?.[ALERT_WORKFLOW_STATUS] === 'open' ); - expect(everySignalOpen).to.eql(true); + expect(everyAlertOpen).to.eql(true); }); - it('should be able to get a count of 10 closed signals when closing 10', async () => { + it('should be able to get a count of 10 closed alerts when closing 10', async () => { const rule = { - ...getRuleForSignalTesting(['auditbeat-*']), + ...getRuleForAlertTesting(['auditbeat-*']), query: 'process.executable: "/usr/bin/sudo"', }; const { id } = await createRule(supertest, log, rule); await waitForRuleSuccess({ supertest, log, id }); - await waitForSignalsToBePresent(supertest, log, 10, [id]); - const signalsOpen = await getSignalsByIds(supertest, log, [id]); - const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + await waitForAlertsToBePresent(supertest, log, 10, [id]); + const alertsOpen = await getAlertsByIds(supertest, log, [id]); + const alertIds = alertsOpen.hits.hits.map((alert) => alert._id); - // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // set all of the alerts to the state of closed. There is no reason to use a waitUntil here // as this route intentionally has a waitFor within it and should only return when the query has // the data. await supertest .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) .set('kbn-xsrf', 'true') - .send(setSignalStatus({ signalIds, status: 'closed' })) + .send(setAlertStatus({ alertIds, status: 'closed' })) .expect(200); - const { body: signalsClosed }: { body: estypes.SearchResponse } = + const { body: alertsClosed }: { body: estypes.SearchResponse } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) .set('kbn-xsrf', 'true') - .send(getQuerySignalIds(signalIds)) + .send(getQueryAlertIds(alertIds)) .expect(200); - expect(signalsClosed.hits.hits.length).to.equal(10); + expect(alertsClosed.hits.hits.length).to.equal(10); }); // Test is failing after changing refresh to false - it.skip('should be able close 10 signals immediately and they all should be closed', async () => { + it.skip('should be able close 10 alerts immediately and they all should be closed', async () => { const rule = { - ...getRuleForSignalTesting(['auditbeat-*']), + ...getRuleForAlertTesting(['auditbeat-*']), query: 'process.executable: "/usr/bin/sudo"', }; const { id } = await createRule(supertest, log, rule); await waitForRuleSuccess({ supertest, log, id }); - await waitForSignalsToBePresent(supertest, log, 10, [id]); - const signalsOpen = await getSignalsByIds(supertest, log, [id]); - const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); + await waitForAlertsToBePresent(supertest, log, 10, [id]); + const alertsOpen = await getAlertsByIds(supertest, log, [id]); + const alertIds = alertsOpen.hits.hits.map((alert) => alert._id); - // set all of the signals to the state of closed. There is no reason to use a waitUntil here + // set all of the alerts to the state of closed. There is no reason to use a waitUntil here // as this route intentionally has a waitFor within it and should only return when the query has // the data. await supertest .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) .set('kbn-xsrf', 'true') - .send(setSignalStatus({ signalIds, status: 'closed' })) + .send(setAlertStatus({ alertIds, status: 'closed' })) .expect(200); - const { body: signalsClosed }: { body: estypes.SearchResponse } = + const { body: alertsClosed }: { body: estypes.SearchResponse } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) .set('kbn-xsrf', 'true') - .send(getQuerySignalIds(signalIds)) + .send(getQueryAlertIds(alertIds)) .expect(200); - const everySignalClosed = signalsClosed.hits.hits.every( + const everyAlertClosed = alertsClosed.hits.hits.every( (hit) => hit._source?.[ALERT_WORKFLOW_STATUS] === 'closed' ); - expect(everySignalClosed).to.eql(true); + expect(everyAlertClosed).to.eql(true); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts.ts similarity index 68% rename from x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts.ts index 2acd4ffab10bc..3b372597cffd3 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts.ts @@ -11,25 +11,25 @@ import { DETECTION_ENGINE_QUERY_SIGNALS_URL, ALERTS_AS_DATA_FIND_URL, } from '@kbn/security-solution-plugin/common/constants'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { getSignalStatus, createSignalsIndex, deleteAllAlerts } from '../../utils'; +import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; +import { getAlertStatus, createAlertsIndex, deleteAllAlerts } from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const log = getService('log'); const es = getService('es'); - describe('query_signals_route and find_alerts_route', () => { + describe('@ess @serverless query_signals_route and find_alerts_route', () => { describe('validation checks', () => { // This fails and should be investigated or removed if it no longer applies - it.skip('should not give errors when querying and the signals index does exist and is empty', async () => { - await createSignalsIndex(supertest, log); + it.skip('should not give errors when querying and the alerts index does exist and is empty', async () => { + await createAlertsIndex(supertest, log); const { body } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) .set('kbn-xsrf', 'true') - .send(getSignalStatus()) + .send(getAlertStatus()) .expect(200); // remove any server generated items that are indeterministic @@ -48,56 +48,19 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('backwards compatibility', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals'); - await createSignalsIndex(supertest, log); - }); - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals'); - await deleteAllAlerts(supertest, log, es); - }); - - it('should be able to filter old signals on host.os.name.caseless using runtime field', async () => { - const query = { - query: { - bool: { - should: [{ match_phrase: { 'host.os.name.caseless': 'windows' } }], - }, - }, - }; - const { body } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(query) - .expect(200); - expect(body.hits.total.value).to.eql(3); - }); - - it('should be able to filter old signals using field aliases', async () => { - const query = { - query: { - bool: { - should: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }], - }, - }, - }; - const { body } = await supertest - .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) - .set('kbn-xsrf', 'true') - .send(query) - .expect(200); - expect(body.hits.total.value).to.eql(3); - }); - }); - describe('runtime fields', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals'); - await createSignalsIndex(supertest, log); + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs', + { + useCreate: true, + docsOnly: true, + } + ); + await createAlertsIndex(supertest, log); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals'); + // await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals'); await deleteAllAlerts(supertest, log, es); }); @@ -129,12 +92,12 @@ export default ({ getService }: FtrProviderContext) => { describe('find_alerts_route', () => { describe('validation checks', () => { // This fails and should be investigated or removed if it no longer applies - it.skip('should not give errors when querying and the signals index does exist and is empty', async () => { - await createSignalsIndex(supertest, log); + it.skip('should not give errors when querying and the alerts index does exist and is empty', async () => { + await createAlertsIndex(supertest, log); const { body } = await supertest .post(ALERTS_AS_DATA_FIND_URL) .set('kbn-xsrf', 'true') - .send({ ...getSignalStatus(), index: '.siem-signals-default' }) + .send({ ...getAlertStatus(), index: '.siem-signals-default' }) .expect(200); // remove any server generated items that are indeterministic @@ -153,10 +116,11 @@ export default ({ getService }: FtrProviderContext) => { }); it('should not give errors when executing security solution histogram aggs', async () => { - await createSignalsIndex(supertest, log); + await createAlertsIndex(supertest, log); await supertest .post(ALERTS_AS_DATA_FIND_URL) .set('kbn-xsrf', 'true') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send({ index: '.siem-signals-default', aggs: { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts_backword_compatibility.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts_backword_compatibility.ts new file mode 100644 index 0000000000000..76f85dd323976 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/alerts/query_alerts_backword_compatibility.ts @@ -0,0 +1,62 @@ +/* + * Copyright 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 { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants'; +import { createAlertsIndex, deleteAllAlerts } from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const log = getService('log'); + const es = getService('es'); + + describe('@ess query_alerts_backword_compatibility', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals'); + await createAlertsIndex(supertest, log); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals'); + await deleteAllAlerts(supertest, log, es); + }); + + it('should be able to filter old alerts on host.os.name.caseless using runtime field', async () => { + const query = { + query: { + bool: { + should: [{ match_phrase: { 'host.os.name.caseless': 'windows' } }], + }, + }, + }; + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(query) + .expect(200); + expect(body.hits.total.value).to.eql(3); + }); + + it('should be able to filter old alerts using field aliases', async () => { + const query = { + query: { + bool: { + should: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }], + }, + }, + }; + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(query) + .expect(200); + expect(body.hits.total.value).to.eql(3); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/ess.config.ts new file mode 100644 index 0000000000000..b980aef5f783a --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/ess.config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.basic') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine ESS - Basic Integration Tests', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/serverless.config.ts new file mode 100644 index 0000000000000..8a4199ccfb44d --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/serverless.config.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../../../../../config/serverless/config.base.essentials'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine Serverless - Essentials Integration Tests', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/index.ts new file mode 100644 index 0000000000000..296cec0e06448 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Detection Engine Basic and Essentials API', function () { + loadTestFile(require.resolve('./rules/create_rules')); + loadTestFile(require.resolve('./rules/create_ml_rules_privileges')); + loadTestFile(require.resolve('./alerts/open_close_alerts')); + loadTestFile(require.resolve('./alerts/query_alerts')); + loadTestFile(require.resolve('./alerts/query_alerts_backword_compatibility')); + }); +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_ml_rules_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_ml_rules_privileges.ts new file mode 100644 index 0000000000000..0b4bcea421c70 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_ml_rules_privileges.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from 'expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; + +import { + createAlertsIndex, + deleteAllRules, + removeServerGeneratedProperties, + getSimpleMlRule, + deleteAllAlerts, + updateUsername, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + // TODO: add a new service + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); + const isServerless = config.get('serverless'); + const dataPathBuilder = new EsArchivePathBuilder(isServerless); + const auditbeatPath = dataPathBuilder.getPath('auditbeat/hosts'); + + describe('create_ml_rules', () => { + describe('Creating Machine Learning rules', () => { + before(async () => { + await esArchiver.load(auditbeatPath); + }); + + after(async () => { + await esArchiver.unload(auditbeatPath); + }); + + beforeEach(async () => { + await createAlertsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteAllAlerts(supertest, log, es); + await deleteAllRules(supertest, log); + }); + + it('@ess should give a 403 when trying to create a single Machine Learning rule since the license is basic', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .send(getSimpleMlRule()) + .expect(403); + + const bodyToCompare = removeServerGeneratedProperties(body); + expect(bodyToCompare).toEqual({ + message: 'Your license does not support machine learning. Please upgrade your license.', + status_code: 403, + }); + }); + it('@serverless should give a 200 when trying to create a single Machine Learning rule since the license is essentials', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .send(getSimpleMlRule()) + .expect(200); + + const bodyToCompare = removeServerGeneratedProperties(body); + const expectedRule = updateUsername(getSimpleMlRule(), ELASTICSEARCH_USERNAME); + expect(bodyToCompare).toEqual(expect.objectContaining(expectedRule)); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_rules.ts similarity index 55% rename from x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_rules.ts index fbbef20984642..6a3fff87611da 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/rules/create_rules.ts @@ -6,42 +6,49 @@ */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; + import { - createSignalsIndex, + createAlertsIndex, deleteAllRules, getSimpleRule, - getSimpleRuleOutput, getSimpleRuleOutputWithoutRuleId, getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, - getSimpleMlRule, deleteAllAlerts, -} from '../../utils'; + updateUsername, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; +import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); const log = getService('log'); const es = getService('es'); - - describe('create_rules', () => { + // TODO: add a new service + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); + const isServerless = config.get('serverless'); + const dataPathBuilder = new EsArchivePathBuilder(isServerless); + const auditbeatPath = dataPathBuilder.getPath('auditbeat/hosts'); + + describe('@ess @serverless create_rules', () => { describe('creating rules', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load(auditbeatPath); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload(auditbeatPath); }); beforeEach(async () => { - await createSignalsIndex(supertest, log); + await createAlertsIndex(supertest, log); }); afterEach(async () => { @@ -53,12 +60,14 @@ export default ({ getService }: FtrProviderContext) => { const { body } = await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .send(getSimpleRule()) .expect(200); const bodyToCompare = removeServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getSimpleRuleOutput()); + const expectedRule = updateUsername(bodyToCompare, ELASTICSEARCH_USERNAME); + + expect(bodyToCompare).to.eql(expectedRule); }); it('should create a single rule without an input index', async () => { @@ -72,90 +81,49 @@ export default ({ getService }: FtrProviderContext) => { type: 'query', query: 'user.name: root or user.name: admin', }; - const expected = { - actions: [], - author: [], - created_by: 'elastic', - description: 'Simple Rule Query', - enabled: true, - false_positives: [], - from: 'now-6m', - immutable: false, - interval: '5m', - rule_id: 'rule-1', - language: 'kuery', - output_index: '', - max_signals: 100, - risk_score: 1, - risk_score_mapping: [], - name: 'Simple Rule Query', - query: 'user.name: root or user.name: admin', - references: [], - related_integrations: [], - required_fields: [], - setup: '', - severity: 'high', - severity_mapping: [], - updated_by: 'elastic', - tags: [], - to: 'now', - type: 'query', - threat: [], - exceptions_list: [], - version: 1, - revision: 0, - }; const { body } = await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .send(rule) .expect(200); const bodyToCompare = removeServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(expected); + const expectedRule = updateUsername(bodyToCompare, ELASTICSEARCH_USERNAME); + + expect(bodyToCompare).to.eql(expectedRule); }); it('should create a single rule without a rule_id', async () => { const { body } = await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .send(getSimpleRuleWithoutRuleId()) .expect(200); const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body); - expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId()); - }); - - it('should give a 403 when trying to create a single Machine Learning rule since the license is basic', async () => { - const { body } = await supertest - .post(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send(getSimpleMlRule()) - .expect(403); + const expectedRule = updateUsername( + getSimpleRuleOutputWithoutRuleId(), + ELASTICSEARCH_USERNAME + ); - const bodyToCompare = removeServerGeneratedProperties(body); - expect(bodyToCompare).to.eql({ - message: 'Your license does not support machine learning. Please upgrade your license.', - status_code: 403, - }); + expect(bodyToCompare).to.eql(expectedRule); }); it('should cause a 409 conflict if we attempt to create the same rule_id twice', async () => { await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .send(getSimpleRule()) .expect(200); const { body } = await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .send(getSimpleRule()) .expect(409); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts index cec8d1cca41b5..e508918b0538d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts @@ -16,7 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...functionalConfig.getAll(), testFiles: [require.resolve('..')], junit: { - reportName: 'Detection Engine ESS/Actions API Integration Tests', + reportName: 'Detection Engine ESS - Actions API Integration Tests', }, }; } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts index 66edc0eef7f30..ea876833ea839 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts @@ -10,6 +10,6 @@ import { createTestConfig } from '../../../../../config/serverless/config.base'; export default createTestConfig({ testFiles: [require.resolve('..')], junit: { - reportName: 'Detection Engine Serverless/Actions API Integration Tests', + reportName: 'Detection Engine Serverless - Actions API Integration Tests', }, }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/date.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/date.ts index 3ccad9a60e943..c3cedb6daf88f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/date.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/date.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/double.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/double.ts index 20efdb98b631e..9f2673b8542a3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/double.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/double.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/float.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/float.ts index 25d7d2e83c77f..850276622cc6e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/float.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/float.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/integer.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/integer.ts index 5df6119486113..fe26c4a2d729e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/integer.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/date_numeric_types/integer.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip.ts index 9e73771d11f09..ca09070e46763 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip_array.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip_array.ts index 12c4eb6d55368..0c2808ee252a4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip_array.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips/ip_array.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword.ts index 11289a31b1242..9d4b1bfd80a19 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword_array.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword_array.ts index 58f41321e7a86..284d20adfc3ee 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword_array.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword/keyword_array.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/long/long.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/long/long.ts index 69803854e9306..497f24ec217a8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/long/long.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/long/long.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text.ts index df7a42dc88de2..8713cc8ce859c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text.ts @@ -13,7 +13,7 @@ import { deleteListsIndex, importFile, importTextFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text_array.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text_array.ts index 915d353281f66..8c4a265a182bd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text_array.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/text/text_array.ts @@ -12,7 +12,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../../lists_api_integration/utils'; +} from '../../../../../lists_and_exception_lists/utils'; import { createRule, createRuleWithExceptionEntries, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_endpoint_exceptions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_endpoint_exceptions.ts index 1c647fe52810c..0d908b7449126 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_endpoint_exceptions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_endpoint_exceptions.ts @@ -24,7 +24,7 @@ import { createListsIndex, deleteAllExceptions, deleteListsIndex, -} from '../../../../../../lists_api_integration/utils'; +} from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_rule_exceptions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_rule_exceptions.ts index 3607cba64151e..1f1e4d91d4a09 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_rule_exceptions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/create_rule_exceptions.ts @@ -31,7 +31,7 @@ import { import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties, -} from '../../../../../../lists_api_integration/utils'; +} from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; const getRuleExceptionItemMock = (): CreateRuleExceptionListItemSchema => ({ diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/find_rule_exception_references.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/find_rule_exception_references.ts index a2f996539f199..87f9c4a17914b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/find_rule_exception_references.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/find_rule_exception_references.ts @@ -30,7 +30,7 @@ import { deleteAllAlerts, createAlertsIndex, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_add_edit_comments.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_add_edit_comments.ts index 3ce0aa0bed874..8e36816213cff 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_add_edit_comments.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_add_edit_comments.ts @@ -18,7 +18,7 @@ import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/c import { ROLES } from '@kbn/security-solution-plugin/common/test'; import { getUpdateMinimalExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_exception_list_item_schema.mock'; import { UpdateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { createUserAndRole, deleteUserAndRole, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_rule_exceptions_workflows.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_rule_exceptions_workflows.ts index f62501b026c20..870df90d3e475 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_rule_exceptions_workflows.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/role_based_rule_exceptions_workflows.ts @@ -60,7 +60,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../lists_api_integration/utils'; +} from '../../../../lists_and_exception_lists/utils'; import { createUserAndRole, deleteUserAndRole, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/rule_exception_synchronizations.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/rule_exception_synchronizations.ts index d89055f698ce6..7bbfcf5659420 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/rule_exception_synchronizations.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/rule_exception_synchronizations.ts @@ -30,7 +30,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../lists_api_integration/utils'; +} from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts index a887f07cb2cfe..49ed77a4dc48e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts @@ -431,8 +431,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).toEqual({ error: 'Bad Request', - message: - '[request body]: type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', + message: '[request body]: threshold: Required', statusCode: 400, }); }); @@ -541,7 +540,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).toBe( - '[request body]: investigation_fields: Expected object, received array, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", investigation_fields: Expected object, received array, investigation_fields: Expected object, received array, and 22 more' + '[request body]: investigation_fields: Expected object, received array' ); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts index e869854f0f44a..ede46e40254fd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts @@ -26,7 +26,7 @@ import { removeRandomValuedPropertiesFromAlert, patchRule, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts index 81523755f4eea..6426738b61427 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts @@ -31,7 +31,7 @@ import { deleteAllExceptions, deleteListsIndex, importFile, -} from '../../../../../../lists_api_integration/utils'; +} from '../../../../lists_and_exception_lists/utils'; import { createRule, deleteAllRules, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts index 0aedb19748cf4..5e7f919520058 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts @@ -24,7 +24,7 @@ import { previewRuleWithExceptionEntries, removeRandomValuedPropertiesFromAlert, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts index a1241c60f5ccf..19c02fe389fe4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts @@ -46,7 +46,7 @@ import { } from '@kbn/security-solution-plugin/common/constants'; import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils'; import moment from 'moment'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { createExceptionList, createExceptionListItem, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/all_types.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/all_types.ts index 26ac4627a0260..59da2429f01e8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/all_types.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/all_types.ts @@ -14,7 +14,7 @@ import { getSecurityTelemetryStats, removeTimeFieldsFromTelemetryStats, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/detection_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/detection_rules.ts index bed9457c34707..1298e9aeb9eaa 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/detection_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/detection_rules.ts @@ -23,7 +23,7 @@ import { createExceptionListItem, removeTimeFieldsFromTelemetryStats, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/security_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/security_lists.ts index d654cba35902f..3a4e8cb4c3ea3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/security_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/task_based/security_lists.ts @@ -20,7 +20,7 @@ import { createExceptionList, removeTimeFieldsFromTelemetryStats, } from '../../../utils'; -import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils'; +import { deleteAllExceptions } from '../../../../lists_and_exception_lists/utils'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { diff --git a/x-pack/test/detection_engine_api_integration/utils/get_signal_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alert_status.ts similarity index 89% rename from x-pack/test/detection_engine_api_integration/utils/get_signal_status.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alert_status.ts index d3c922c30ccc6..212aefd4593f4 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_signal_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alert_status.ts @@ -5,6 +5,6 @@ * 2.0. */ -export const getSignalStatus = () => ({ +export const getAlertStatus = () => ({ aggs: { statuses: { terms: { field: 'kibana.alert.workflow_status', size: 10 } } }, }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/index.ts index 975c6ffa509cc..e78bfa1922d36 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/index.ts @@ -20,4 +20,5 @@ export * from './get_alert_status_empty_response'; export * from './get_query_alert_ids'; export * from './set_alert_tags'; export * from './get_preview_alerts'; +export * from './get_alert_status'; export * from './migrations'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_without_rule_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_without_rule_id.ts new file mode 100644 index 0000000000000..56b5ab66773bb --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_without_rule_id.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 { getSimpleRuleOutput } from './get_simple_rule_output'; +import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties'; + +/** + * This is the typical output of a simple rule that Kibana will output with all the defaults except + * for all the server generated properties such as created_by. Useful for testing end to end tests. + */ +export const getSimpleRuleOutputWithoutRuleId = ( + ruleId = 'rule-1' +): Omit => { + const rule = getSimpleRuleOutput(ruleId); + const { rule_id: rId, ...ruleWithoutRuleId } = rule; + return ruleWithoutRuleId; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_without_rule_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_without_rule_id.ts new file mode 100644 index 0000000000000..ad6ab7803ec21 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_without_rule_id.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { getSimpleRule } from './get_simple_rule'; + +/** + * This is a typical simple rule for testing that is easy for most basic testing + */ +export const getSimpleRuleWithoutRuleId = (): RuleCreateProps => { + const simpleRule = getSimpleRule(); + // eslint-disable-next-line @typescript-eslint/naming-convention + const { rule_id, ...ruleWithoutId } = simpleRule; + return ruleWithoutId; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts index db5ccb307395e..04c4cb778ef4a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts @@ -39,5 +39,8 @@ export * from './generate_event'; export * from './create_legacy_rule_action'; export * from './get_simple_threat_match'; export * from './get_simple_ml_rule'; +export * from './remove_server_generated_properties_including_rule_id'; +export * from './get_simple_rule_output_without_rule_id'; +export * from './get_simple_rule_without_rule_id'; export * from './prebuilt_rules'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/remove_server_generated_properties_including_rule_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/remove_server_generated_properties_including_rule_id.ts new file mode 100644 index 0000000000000..1b57b5663ec23 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/remove_server_generated_properties_including_rule_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 type { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine'; + +import { removeServerGeneratedProperties } from './remove_server_generated_properties'; + +/** + * This will remove server generated properties such as date times, etc... including the rule_id + * @param rule Rule to pass in to remove typical server generated properties + */ +export const removeServerGeneratedPropertiesIncludingRuleId = ( + rule: RuleResponse +): Partial => { + const ruleWithRemovedProperties = removeServerGeneratedProperties(rule); + // eslint-disable-next-line @typescript-eslint/naming-convention + const { rule_id, ...additionalRuledIdRemoved } = ruleWithRemovedProperties; + return additionalRuledIdRemoved; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/asset_criticality.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/asset_criticality.ts new file mode 100644 index 0000000000000..8953d9986c035 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/asset_criticality.ts @@ -0,0 +1,85 @@ +/* + * Copyright 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 { + cleanRiskEngine, + cleanAssetCriticality, + assetCriticalityRouteHelpersFactory, +} from '../../utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const es = getService('es'); + const kibanaServer = getService('kibanaServer'); + const log = getService('log'); + const supertest = getService('supertest'); + const assetCriticalityRoutes = assetCriticalityRouteHelpersFactory(supertest); + + describe('@ess @serverless @skipInQA asset_criticality Asset Criticality APIs', () => { + beforeEach(async () => { + await cleanRiskEngine({ kibanaServer, es, log }); + await cleanAssetCriticality({ log, es }); + }); + + afterEach(async () => { + await cleanRiskEngine({ kibanaServer, es, log }); + await cleanAssetCriticality({ log, es }); + }); + + describe('initialisation of resources', () => { + it('should has index installed on status api call', async () => { + const assetCriticalityIndex = '.asset-criticality.asset-criticality-default'; + + let assetCriticalityIndexExist; + + try { + assetCriticalityIndexExist = await es.indices.exists({ + index: assetCriticalityIndex, + }); + } catch (e) { + assetCriticalityIndexExist = false; + } + + expect(assetCriticalityIndexExist).to.eql(false); + + const statusResponse = await assetCriticalityRoutes.status(); + + expect(statusResponse.body).to.eql({ + asset_criticality_resources_installed: true, + }); + + const assetCriticalityIndexResult = await es.indices.get({ + index: assetCriticalityIndex, + }); + + expect( + assetCriticalityIndexResult['.asset-criticality.asset-criticality-default']?.mappings + ).to.eql({ + dynamic: 'strict', + properties: { + '@timestamp': { + type: 'date', + }, + criticality_level: { + type: 'keyword', + }, + id_field: { + type: 'keyword', + }, + id_value: { + type: 'keyword', + }, + updated_at: { + type: 'date', + }, + }, + }); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/ess.config.ts index bb4e078746d58..bb4e5bc80dd96 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/ess.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/ess.config.ts @@ -13,6 +13,16 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...functionalConfig.getAll(), + kbnTestServer: { + ...functionalConfig.get('kbnTestServer'), + serverArgs: [ + ...functionalConfig.get('kbnTestServer.serverArgs'), + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'entityAnalyticsAssetCriticalityEnabled', + 'riskEnginePrivilegesRouteEnabled', + ])}`, + ], + }, testFiles: [require.resolve('..')], junit: { reportName: 'Entity Analytics API Integration Tests - ESS - Risk Engine', diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/serverless.config.ts index e28df5b9c3506..b9add84ac202b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/configs/serverless.config.ts @@ -8,6 +8,12 @@ import { createTestConfig } from '../../../../../config/serverless/config.base'; export default createTestConfig({ + kbnTestServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'entityAnalyticsAssetCriticalityEnabled', + 'riskEnginePrivilegesRouteEnabled', + ])}`, + ], testFiles: [require.resolve('..')], junit: { reportName: 'Entity Analytics API Integration Tests - Serverless - Risk Engine', diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/index.ts index 878725cd32f9a..a01e8ba9de52b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/index.ts @@ -15,5 +15,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./risk_scoring_task/task_execution')); loadTestFile(require.resolve('./risk_scoring_task/task_execution_nondefault_spaces')); loadTestFile(require.resolve('./telemetry_usage')); + loadTestFile(require.resolve('./risk_engine_privileges')); + loadTestFile(require.resolve('./asset_criticality')); }); } diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_engine_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_engine_privileges.ts new file mode 100644 index 0000000000000..aa6a604dc0c32 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_engine_privileges.ts @@ -0,0 +1,222 @@ +/* + * Copyright 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 { riskEngineRouteHelpersFactoryNoAuth } from '../../utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +const USER_PASSWORD = 'changeme'; +const ROLES = [ + { + name: 'security_feature_read', + privileges: { + kibana: [ + { + feature: { + siem: ['read'], + }, + spaces: ['default'], + }, + ], + }, + }, + { + name: 'cluster_manage_index_templates', + privileges: { + elasticsearch: { + cluster: ['manage_index_templates'], + }, + }, + }, + { + name: 'cluster_manage_transform', + privileges: { + elasticsearch: { + cluster: ['manage_transform'], + }, + }, + }, + { + name: 'risk_score_index_read', + privileges: { + elasticsearch: { + indices: [ + { + names: ['risk-score.risk-score-*'], + privileges: ['read'], + }, + ], + }, + }, + }, + { + name: 'risk_score_index_write', + privileges: { + elasticsearch: { + indices: [ + { + names: ['risk-score.risk-score-*'], + privileges: ['write'], + }, + ], + }, + }, + }, +]; + +const ALL_ROLE_NAMES = ROLES.map((role) => role.name); + +const allRolesExcept = (role: string) => ALL_ROLE_NAMES.filter((r) => r !== role); + +const USERNAME_TO_ROLES = { + no_cluster_manage_index_templates: allRolesExcept('cluster_manage_index_templates'), + no_cluster_manage_transform: allRolesExcept('cluster_manage_transform'), + no_risk_score_index_read: allRolesExcept('risk_score_index_read'), + no_risk_score_index_write: allRolesExcept('risk_score_index_write'), + all: ALL_ROLE_NAMES, +}; + +export default ({ getService }: FtrProviderContext) => { + describe('@ess privileges_apis', () => { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const riskEngineRoutesNoAuth = riskEngineRouteHelpersFactoryNoAuth(supertestWithoutAuth); + const security = getService('security'); + + const createRole = async ({ name, privileges }: { name: string; privileges: any }) => { + return await security.role.create(name, privileges); + }; + + const createUser = async ({ + username, + password, + roles, + }: { + username: string; + password: string; + roles: string[]; + }) => { + return await security.user.create(username, { + password, + roles, + full_name: username.replace('_', ' '), + email: `${username}@elastic.co`, + }); + }; + + async function createPrivilegeTestUsers() { + const rolePromises = ROLES.map((role) => createRole(role)); + + await Promise.all(rolePromises); + const userPromises = Object.entries(USERNAME_TO_ROLES).map(([username, roles]) => + createUser({ username, roles, password: USER_PASSWORD }) + ); + + return Promise.all(userPromises); + } + + const getPrivilegesForUsername = async (username: string) => + riskEngineRoutesNoAuth.privilegesForUser({ + username, + password: USER_PASSWORD, + }); + before(async () => { + await createPrivilegeTestUsers(); + }); + + describe('Risk engine privileges API', () => { + it('should return has_all_required true for user with all risk engine privileges', async () => { + const { body } = await getPrivilegesForUsername('all'); + expect(body.has_all_required).to.eql(true); + expect(body.privileges).to.eql({ + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: true, + }, + }, + }, + }); + }); + it('should return has_all_required false for user with no write access to risk indices', async () => { + const { body } = await getPrivilegesForUsername('no_risk_score_index_write'); + expect(body.has_all_required).to.eql(false); + expect(body.privileges).to.eql({ + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: false, + }, + }, + }, + }); + }); + it('should return has_all_required false for user with no read access to risk indices', async () => { + const { body } = await getPrivilegesForUsername('no_risk_score_index_read'); + expect(body.has_all_required).to.eql(false); + expect(body.privileges).to.eql({ + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: false, + write: true, + }, + }, + }, + }); + }); + it('should return has_all_required false for user with no cluster manage transform privilege', async () => { + const { body } = await getPrivilegesForUsername('no_cluster_manage_transform'); + expect(body.has_all_required).to.eql(false); + expect(body.privileges).to.eql({ + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: false, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: true, + }, + }, + }, + }); + }); + it('should return has_all_required false for user with no cluster manage index templates privilege', async () => { + const { body } = await getPrivilegesForUsername('no_cluster_manage_index_templates'); + expect(body.has_all_required).to.eql(false); + expect(body.privileges).to.eql({ + elasticsearch: { + cluster: { + manage_index_templates: false, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: true, + }, + }, + }, + }); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution.ts index 53c70bc90efb9..7ebfb568e8cd3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution.ts @@ -179,7 +179,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - describe('with some alerts containing hosts and others containing users', () => { + // FLAKY: https://github.com/elastic/kibana/issues/171132 + describe.skip('with some alerts containing hosts and others containing users', () => { let hostId: string; let userId: string; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution_nondefault_spaces.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution_nondefault_spaces.ts index cf9bf98f88d9d..d869fae2f3f95 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution_nondefault_spaces.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/risk_engine/risk_scoring_task/task_execution_nondefault_spaces.ts @@ -23,7 +23,7 @@ import { deleteRiskScoreIndices, } from '../../../utils'; -import { FtrProviderContextWithSpaces } from './ftr_provider_context_with_spaces'; +import { FtrProviderContextWithSpaces } from '../../../../../ftr_provider_context_with_spaces'; export default ({ getService }: FtrProviderContextWithSpaces): void => { const supertest = getService('supertest'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts new file mode 100644 index 0000000000000..2bc7de4a895f5 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.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 SuperTest from 'supertest'; +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; +import { ASSET_CRITICALITY_STATUS_URL } from '@kbn/security-solution-plugin/common/constants'; +import type { Client } from '@elastic/elasticsearch'; +import type { ToolingLog } from '@kbn/tooling-log'; +import { routeWithNamespace } from '../../detections_response/utils'; + +export const cleanAssetCriticality = async ({ + log, + es, + namespace = 'default', +}: { + log: ToolingLog; + es: Client; + namespace?: string; +}) => { + try { + await Promise.allSettled([ + es.indices.delete({ + index: [`.asset-criticality.asset-criticality-${namespace}`], + }), + ]); + } catch (e) { + log.warning(`Error deleting asset criticality index: ${e.message}`); + } +}; + +export const assetCriticalityRouteHelpersFactory = ( + supertest: SuperTest.SuperTest, + namespace?: string +) => ({ + status: async () => + await supertest + .get(routeWithNamespace(ASSET_CRITICALITY_STATUS_URL, namespace)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send() + .expect(200), +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts index 00eb2adafd2e8..dacdf5052c912 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts @@ -6,3 +6,4 @@ */ export * from './risk_engine'; export * from './get_risk_engine_stats'; +export * from './asset_criticality'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts index 48c7763d7a9d6..103577482a771 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts @@ -21,6 +21,7 @@ import { RISK_ENGINE_DISABLE_URL, RISK_ENGINE_ENABLE_URL, RISK_ENGINE_STATUS_URL, + RISK_ENGINE_PRIVILEGES_URL, } from '@kbn/security-solution-plugin/common/constants'; import { createRule, @@ -504,6 +505,20 @@ export const riskEngineRouteHelpersFactory = ( .expect(200), }); +export const riskEngineRouteHelpersFactoryNoAuth = ( + supertestWithoutAuth: SuperTest.SuperTest, + namespace?: string +) => ({ + privilegesForUser: async ({ username, password }: { username: string; password: string }) => + await supertestWithoutAuth + .get(RISK_ENGINE_PRIVILEGES_URL) + .auth(username, password) + .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send() + .expect(200), +}); + export const installLegacyRiskScore = async ({ supertest, }: { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/ess.config.ts new file mode 100644 index 0000000000000..366e0b956e370 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/ess.config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine ESS - Execption Lists and Items Integration Tests APIS', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/serverless.config.ts new file mode 100644 index 0000000000000..bb1410030e0db --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/serverless.config.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../../../../../config/serverless/config.base'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine Serverless - Execption Lists and Items Integration Tests APIS', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/index.ts new file mode 100644 index 0000000000000..b490143ff28bd --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Exception Lists and Items API', function () { + loadTestFile(require.resolve('./items')); + loadTestFile(require.resolve('./lists')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/create_exception_list_items.ts similarity index 92% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/create_exception_list_items.ts index ab4eb4d802fc9..ad7c413a3c6a7 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/create_exception_list_items.ts @@ -15,21 +15,21 @@ import { getCreateExceptionListItemMinimalSchemaMock, getCreateExceptionListItemMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { removeListItemServerGeneratedProperties, removeExceptionListItemServerGeneratedProperties, -} from '../../utils'; + deleteAllExceptions, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -import { deleteAllExceptions } from '../../utils'; - -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('create_exception_list_items', () => { + describe('@ess @serverless create_exception_list_items', () => { describe('validation errors', () => { it('should give a 404 error that the exception list must exist first before being able to add a list item to the exception list', async () => { const { body } = await supertest @@ -64,7 +64,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should create a match any exception item with multiple case sensitive values', async () => { @@ -93,7 +95,7 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); expect(bodyToCompare).to.eql({ - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), entries, }); }); @@ -113,7 +115,7 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeListItemServerGeneratedProperties(body); const outputList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), item_id: body.item_id, }; expect(bodyToCompare).to.eql(outputList); @@ -163,7 +165,7 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); expect(bodyToCompare).to.eql({ - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), expire_time: datetime, }); }); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/delete_exception_list_items.ts similarity index 88% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/delete_exception_list_items.ts index 1a97ac6db4cb0..4d3b2be7de27f 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/delete_exception_list_items.ts @@ -15,16 +15,21 @@ import { getCreateExceptionListItemMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } from '../../utils'; +import { + deleteAllExceptions, + removeExceptionListItemServerGeneratedProperties, +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('delete_exception_list_items', () => { + describe('@ess @serverless delete_exception_list_items', () => { describe('delete exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -56,7 +61,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should delete a single exception list item using an auto generated id', async () => { @@ -80,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => { .set('kbn-xsrf', 'true') .expect(200); const outputtedList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), item_id: body.item_id, }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/find_exception_list_items.ts similarity index 90% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/find_exception_list_items.ts index 909930d713473..3fcbd476b7547 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/find_exception_list_items.ts @@ -17,16 +17,21 @@ import { getCreateExceptionListMinimalSchemaMock, getCreateExceptionListDetectionSchemaMock, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } from '../../utils'; +import { + deleteAllExceptions, + removeExceptionListItemServerGeneratedProperties, +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('find_exception_list_items', () => { + describe('@ess @serverless find_exception_list_items', () => { describe('find exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -104,7 +109,7 @@ export default ({ getService }: FtrProviderContext): void => { data: [ { comments: [], - created_by: 'elastic', + created_by: ELASTICSEARCH_USERNAME, description: 'some description', entries: [ { @@ -121,7 +126,7 @@ export default ({ getService }: FtrProviderContext): void => { os_types: ['windows'], tags: [], type: 'simple', - updated_by: 'elastic', + updated_by: ELASTICSEARCH_USERNAME, }, ], page: 1, @@ -171,7 +176,9 @@ export default ({ getService }: FtrProviderContext): void => { body.data = [removeExceptionListItemServerGeneratedProperties(body.data[0])]; expect(body).to.eql({ - data: [getExceptionListItemResponseMockWithoutAutoGeneratedValues()], + data: [ + getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), + ], page: 1, per_page: 20, total: 1, diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/index.ts new file mode 100644 index 0000000000000..df84a7a9ee3d5 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Lists API', function () { + loadTestFile(require.resolve('./create_exception_list_items')); + loadTestFile(require.resolve('./read_exception_list_items')); + loadTestFile(require.resolve('./update_exception_list_items')); + loadTestFile(require.resolve('./delete_exception_list_items')); + loadTestFile(require.resolve('./find_exception_list_items')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/read_exception_list_items.ts similarity index 89% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/read_exception_list_items.ts index 42783a461ce7e..a2cffa490194c 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/read_exception_list_items.ts @@ -15,16 +15,20 @@ import { getCreateExceptionListItemMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } from '../../utils'; +import { + deleteAllExceptions, + removeExceptionListItemServerGeneratedProperties, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('read_exception_list_items', () => { + describe('@ess @serverless read_exception_list_items', () => { describe('reading exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -45,7 +49,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single exception list item using id', async () => { @@ -69,7 +75,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single list item with an auto-generated id', async () => { @@ -92,7 +100,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputtedList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), item_id: body.item_id, }; @@ -120,7 +128,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputtedList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), item_id: body.item_id, }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/update_exception_list_items.ts similarity index 97% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/update_exception_list_items.ts index cfb01154be854..6e0a1daa024e1 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/items/update_exception_list_items.ts @@ -19,20 +19,21 @@ import { } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { getUpdateMinimalExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_exception_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties, removeExceptionListServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('update_exception_list_items', () => { + describe('@ess @serverless update_exception_list_items', () => { describe('update exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -252,7 +253,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', }; @@ -292,7 +293,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(), + ...getExceptionListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', item_id: body.item_id, }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/create_exception_lists.ts similarity index 86% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/create_exception_lists.ts index a3c71a8be2fc1..da066b078b0c4 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/create_exception_lists.ts @@ -14,16 +14,17 @@ import { getCreateExceptionListMinimalSchemaMock, getCreateExceptionListMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('create_exception_lists', () => { + describe('@ess @serverless create_exception_lists', () => { describe('creating exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -37,7 +38,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should create a simple exception list without a list_id', async () => { @@ -49,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeExceptionListServerGeneratedProperties(body); const outputtedList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), list_id: bodyToCompare.list_id, }; expect(bodyToCompare).to.eql(outputtedList); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/delete_exception_lists.ts similarity index 89% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/delete_exception_lists.ts index 5361bcb82f6a2..ae109897fb138 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/delete_exception_lists.ts @@ -14,16 +14,18 @@ import { getCreateExceptionListMinimalSchemaMock, getCreateExceptionListMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('delete_exception_lists', () => { + describe('@ess @serverless delete_exception_lists', () => { describe('delete exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -46,7 +48,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should delete a single exception list using an auto generated id', async () => { @@ -64,7 +68,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputtedList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), list_id: body.list_id, }; const bodyToCompare = removeExceptionListServerGeneratedProperties(body); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/duplicate_exception_list.ts similarity index 93% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/duplicate_exception_list.ts index 54e7a89042b02..14a4dcfb65ec7 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/duplicate_exception_list.ts @@ -15,16 +15,18 @@ import { import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('duplicate_exception_lists', () => { + describe('@ess @serverless duplicate_exception_lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); }); @@ -48,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeExceptionListServerGeneratedProperties(body); expect(bodyToCompare).to.eql({ - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), type: 'detection', list_id: body.list_id, name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, @@ -89,7 +91,7 @@ export default ({ getService }: FtrProviderContext) => { const listBodyToCompare = removeExceptionListServerGeneratedProperties(listBody); expect(listBodyToCompare).to.eql({ - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), type: 'detection', list_id: listBody.list_id, name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/export_exception_list.ts similarity index 97% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/export_exception_list.ts index bfde054eb8f1e..726ec6269508f 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/export_exception_list.ts @@ -11,20 +11,19 @@ import { EXCEPTION_LIST_URL, EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysoluti import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { removeExceptionListServerGeneratedProperties, removeExceptionListItemServerGeneratedProperties, binaryToString, deleteAllExceptions, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('export_exception_list_route', () => { + describe('@ess @serverless export_exception_list_route', () => { describe('exporting exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/find_exception_lists.ts similarity index 87% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/find_exception_lists.ts index 349a44c506949..cb405b7b7d642 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/find_exception_lists.ts @@ -10,16 +10,17 @@ import expect from '@kbn/expect'; import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('find_exception_lists', () => { + describe('@ess @serverless find_exception_lists', () => { describe('find exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -57,7 +58,7 @@ export default ({ getService }: FtrProviderContext): void => { body.data = [removeExceptionListServerGeneratedProperties(body.data[0])]; expect(body).to.eql({ - data: [getExceptionResponseMockWithoutAutoGeneratedValues()], + data: [getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME)], page: 1, per_page: 20, total: 1, diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/get_exception_filter.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/get_exception_filter.ts similarity index 92% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/get_exception_filter.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/get_exception_filter.ts index 913ce14db1f00..633262ca1baa4 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/get_exception_filter.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/get_exception_filter.ts @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; - +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; import { INTERNAL_EXCEPTION_FILTER, EXCEPTION_LIST_URL, @@ -18,16 +21,16 @@ import { } from '@kbn/lists-plugin/common/schemas/request/get_exception_filter_schema.mock'; import { getCreateExceptionListItemMinimalSchemaMockWithoutId } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createListsIndex, deleteListsIndex } from '../../utils'; +import { createListsIndex, deleteListsIndex } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('get_exception_filter', () => { + describe('@ess @serverless get_exception_filter', () => { describe('get exception filter', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -41,7 +44,8 @@ export default ({ getService }: FtrProviderContext): void => { const { body } = await supertest .post(`${INTERNAL_EXCEPTION_FILTER}`) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .send(getExceptionFilterFromExceptionItemsSchemaMock()) .expect(200); @@ -122,7 +126,8 @@ export default ({ getService }: FtrProviderContext): void => { const { body } = await supertest .post(`${INTERNAL_EXCEPTION_FILTER}`) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .send(getExceptionFilterFromExceptionIdsSchemaMock()) .expect(200); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/import_exceptions.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/import_exceptions.ts similarity index 99% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/import_exceptions.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/import_exceptions.ts index 8736ad75eeb6f..bc191618914d2 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/import_exceptions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/import_exceptions.ts @@ -14,15 +14,15 @@ import { getImportExceptionsListItemSchemaMock, getImportExceptionsListSchemaMock, } from '@kbn/lists-plugin/common/schemas/request/import_exceptions_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions } from '../../utils'; +import { deleteAllExceptions } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('import_exceptions', () => { + describe('@ess @serverless import_exceptions', () => { beforeEach(async () => { await deleteAllExceptions(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/index.ts new file mode 100644 index 0000000000000..0e27b93b93b6f --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/index.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 { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Lists API', function () { + loadTestFile(require.resolve('./duplicate_exception_list')); + loadTestFile(require.resolve('./get_exception_filter')); + loadTestFile(require.resolve('./import_exceptions')); + loadTestFile(require.resolve('./export_exception_list')); + loadTestFile(require.resolve('./create_exception_lists')); + loadTestFile(require.resolve('./read_exception_lists')); + loadTestFile(require.resolve('./update_exception_lists')); + loadTestFile(require.resolve('./delete_exception_lists')); + loadTestFile(require.resolve('./find_exception_lists')); + loadTestFile(require.resolve('./summary_exception_lists')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/read_exception_lists.ts similarity index 87% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/read_exception_lists.ts index 31293e97cb078..b2061928e1759 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/read_exception_lists.ts @@ -14,16 +14,18 @@ import { getCreateExceptionListMinimalSchemaMock, getCreateExceptionListMinimalSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('read_exception_lists', () => { + describe('@ess @serverless read_exception_lists', () => { describe('reading exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -43,7 +45,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single exception list using id', async () => { @@ -60,7 +64,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeExceptionListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getExceptionResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single list with an auto-generated list_id', async () => { @@ -77,7 +83,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputtedList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), list_id: body.list_id, }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/summary_exception_lists.ts similarity index 95% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/summary_exception_lists.ts index d5848e7ee44be..2c0c9780714f7 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/summary_exception_lists.ts @@ -12,18 +12,18 @@ import { EXCEPTION_LIST_URL, EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysoluti import { LIST_ID } from '@kbn/lists-plugin/common/constants.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createListsIndex, deleteListsIndex, deleteAllExceptions } from '../../utils'; +import { createListsIndex, deleteListsIndex, deleteAllExceptions } from '../../../utils'; interface SummaryResponseType { body: ExceptionListSummarySchema; } -// eslint-disable-next-line import/no-default-export +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); - describe('summary_exception_lists', () => { + describe('@ess @serverless summary_exception_lists', () => { describe('summary exception lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/update_exception_lists.ts similarity index 94% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/update_exception_lists.ts index d4c5cd422e772..1a5aed16b128c 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/lists/update_exception_lists.ts @@ -15,16 +15,18 @@ import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; import { getUpdateMinimalExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_exception_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('update_exception_lists', () => { + describe('@ess @serverless update_exception_lists', () => { describe('update exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); @@ -51,7 +53,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -84,7 +86,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', list_id: body.list_id, version: 2, @@ -116,7 +118,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getExceptionResponseMockWithoutAutoGeneratedValues(), + ...getExceptionResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', description: 'some other description', version: 2, diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/ess.config.ts new file mode 100644 index 0000000000000..522c44b41d85a --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/ess.config.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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine ESS - Lists and Items Integration Tests APIS', + }, + }; +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/config.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/serverless.config.ts similarity index 50% rename from x-pack/test/lists_api_integration/security_and_spaces/config.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/serverless.config.ts index 8b7e43945c8a2..7e324d6e29836 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/serverless.config.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { createTestConfig } from '../common/config'; +import { createTestConfig } from '../../../../../config/serverless/config.base'; -// eslint-disable-next-line import/no-default-export -export default createTestConfig('security_and_spaces', { - disabledPlugins: [], - license: 'trial', - ssl: true, +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine Serverless - Lists and Items Integration Tests APIS', + }, }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/index.ts new file mode 100644 index 0000000000000..9422d4b2315f9 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Lists and Items API', function () { + loadTestFile(require.resolve('./items')); + loadTestFile(require.resolve('./lists')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/create_list_items.ts similarity index 86% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/create_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/create_list_items.ts index 40be89af0284a..f174fdff3f774 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/create_list_items.ts @@ -16,20 +16,21 @@ import { getCreateMinimalListItemSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('create_list_items', () => { + describe('@ess @serverless create_list_items', () => { describe('validation errors', () => { it('should give a 404 error that the list must exist first before being able to add a list item', async () => { const { body } = await supertest @@ -68,7 +69,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should create a simple list item without an id', async () => { @@ -85,7 +88,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should cause a 409 conflict if we attempt to create the same list item twice', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/delete_list_items.ts similarity index 85% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/delete_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/delete_list_items.ts index d317e647174f7..f17d950a10dc1 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/delete_list_items.ts @@ -11,20 +11,21 @@ import { LIST_URL, LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('delete_list_items', () => { + describe('@ess @serverless delete_list_items', () => { describe('deleting list items', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -56,7 +57,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should delete a single list using an auto generated id', async () => { @@ -81,7 +84,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should return an error if the id does not exist when trying to delete it', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/export_list_items.ts similarity index 95% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/export_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/export_list_items.ts index fe471e2794b43..b9c99a49af931 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/export_list_items.ts @@ -11,16 +11,16 @@ import { LIST_ITEM_URL, LIST_URL } from '@kbn/securitysolution-list-constants'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { LIST_ID, NAME } from '@kbn/lists-plugin/common/constants.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createListsIndex, deleteListsIndex, binaryToString } from '../../utils'; +import { createListsIndex, deleteListsIndex, binaryToString } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('export_list_items', () => { + describe('@ess @serverless export_list_items', () => { describe('exporting lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/find_list_items.ts similarity index 92% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/find_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/find_list_items.ts index c4ecce7d4d0e7..0c8aa8aa50fa8 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/find_list_items.ts @@ -12,20 +12,22 @@ import { LIST_ITEM_ID, LIST_ID } from '@kbn/lists-plugin/common/constants.mock'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('find_list_items', () => { + describe('@ess @serverless find_list_items', () => { describe('find list items', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -107,7 +109,7 @@ export default ({ getService }: FtrProviderContext): void => { // cursor is a constant changing value so we have to delete it as well. delete body.cursor; expect(body).to.eql({ - data: [getListItemResponseMockWithoutAutoGeneratedValues()], + data: [getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME)], page: 1, per_page: 20, total: 1, diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/import_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items.ts similarity index 74% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/import_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items.ts index 89ae216adc865..99a96ae9052b1 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/import_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items.ts @@ -11,7 +11,6 @@ import { LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; import { getImportListItemAsBuffer } from '@kbn/lists-plugin/common/schemas/request/import_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, @@ -19,16 +18,16 @@ import { removeListServerGeneratedProperties, removeListItemServerGeneratedProperties, waitFor, - createListsIndices, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('import_list_items', () => { + describe('@ess @serverless import_list_items', () => { describe('importing list items without an index', () => { it('should not import a list item if the index does not exist yet', async () => { const { body } = await supertest @@ -74,7 +73,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyToCompare = removeListServerGeneratedProperties(body); const outputtedList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'list_items.txt', description: 'File uploaded from file system of list_items.txt', }; @@ -107,41 +106,11 @@ export default ({ getService }: FtrProviderContext): void => { const bodyToCompare = removeListItemServerGeneratedProperties(body[0]); const outputtedList: Partial = { - ...getListItemResponseMockWithoutAutoGeneratedValues(), + ...getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), list_id: 'list_items.txt', }; expect(bodyToCompare).to.eql(outputtedList); }); - - describe('legacy index (before migration to data streams)', () => { - beforeEach(async () => { - await deleteListsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteListsIndex(supertest, log); - }); - - it('should import list to legacy index and migrate it', async () => { - // create legacy indices - await createListsIndices(es); - - const { body } = await supertest - .post(`${LIST_ITEM_URL}/_import?type=ip`) - .set('kbn-xsrf', 'true') - .attach('file', getImportListItemAsBuffer(['127.0.0.1', '127.0.0.2']), 'list_items.txt') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200); - - const bodyToCompare = removeListServerGeneratedProperties(body); - const outputtedList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), - name: 'list_items.txt', - description: 'File uploaded from file system of list_items.txt', - }; - expect(bodyToCompare).to.eql(outputtedList); - }); - }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items_migrations.ts new file mode 100644 index 0000000000000..cd614bd07e359 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/import_list_items_migrations.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; +import { getImportListItemAsBuffer } from '@kbn/lists-plugin/common/schemas/request/import_list_item_schema.mock'; + +import { + deleteListsIndex, + removeListServerGeneratedProperties, + createListsIndices, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); + + describe('@ess import_list_items_migrations', () => { + describe('import list to legacy index and migrate it', () => { + describe('legacy index (before migration to data streams)', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + + it('should import list to legacy index and migrate it', async () => { + // create legacy indices + await createListsIndices(es); + + const { body } = await supertest + .post(`${LIST_ITEM_URL}/_import?type=ip`) + .set('kbn-xsrf', 'true') + .attach('file', getImportListItemAsBuffer(['127.0.0.1', '127.0.0.2']), 'list_items.txt') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + + const bodyToCompare = removeListServerGeneratedProperties(body); + const outputtedList: Partial = { + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), + name: 'list_items.txt', + description: 'File uploaded from file system of list_items.txt', + }; + expect(bodyToCompare).to.eql(outputtedList); + }); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/index.ts new file mode 100644 index 0000000000000..d89f7ca5566df --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/index.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 { FtrProviderContext } from '../../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Lists API', function () { + loadTestFile(require.resolve('./create_list_items')); + loadTestFile(require.resolve('./patch_list_items')); + loadTestFile(require.resolve('./patch_list_items_migrations')); + loadTestFile(require.resolve('./read_list_items')); + loadTestFile(require.resolve('./update_list_items')); + loadTestFile(require.resolve('./update_list_items_migrations')); + loadTestFile(require.resolve('./delete_list_items')); + loadTestFile(require.resolve('./find_list_items')); + loadTestFile(require.resolve('./import_list_items')); + loadTestFile(require.resolve('./import_list_items_migrations')); + loadTestFile(require.resolve('./export_list_items')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/patch_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items.ts similarity index 73% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/patch_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items.ts index 0827b5813b6c4..01a9d332ba355 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/patch_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items.ts @@ -12,30 +12,27 @@ import type { CreateListItemSchema, ListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import { LIST_URL, LIST_ITEM_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; +import { LIST_URL, LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getUpdateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, - createListsIndices, - createListBypassingChecks, - createListItemBypassingChecks, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); const retry = getService('retry'); - const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('patch_list_items', () => { + describe('@ess @serverless patch_list_items', () => { describe('patch list items', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -73,7 +70,7 @@ export default ({ getService }: FtrProviderContext) => { .send(patchListItemPayload); const outputListItem: Partial = { - ...getListItemResponseMockWithoutAutoGeneratedValues(), + ...getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), value: '192.168.0.2', }; const bodyToCompare = removeListItemServerGeneratedProperties(body); @@ -123,7 +120,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputListItem: Partial = { - ...getListItemResponseMockWithoutAutoGeneratedValues(), + ...getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), value: '192.168.0.2', }; const bodyToCompare = { @@ -219,63 +216,6 @@ export default ({ getService }: FtrProviderContext) => { message: 'list item id: "some-other-id" not found', }); }); - - describe('legacy list items index (list created before migration to data stream)', () => { - beforeEach(async () => { - await deleteListsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteListsIndex(supertest, log); - }); - it('should patch list item that was created in legacy index and migrated through LIST_INDEX request', async () => { - const listId = 'random-list'; - const listItemId = 'random-list-item'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); - // migrates old indices to data streams - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); - - const patchPayload: PatchListItemSchema = { - id: listItemId, - value: 'new one', - }; - - const { body } = await supertest - .patch(LIST_ITEM_URL) - .set('kbn-xsrf', 'true') - .send(patchPayload) - .expect(200); - - expect(body.value).to.be('new one'); - }); - - it('should patch list item that was created in legacy index and not yet migrated', async () => { - const listId = 'random-list'; - const listItemId = 'random-list-item'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); - - const patchPayload: PatchListItemSchema = { - id: listItemId, - value: 'new one', - }; - - const { body } = await supertest - .patch(LIST_ITEM_URL) - .set('kbn-xsrf', 'true') - .send(patchPayload) - .expect(200); - - expect(body.value).to.be('new one'); - }); - }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items_migrations.ts new file mode 100644 index 0000000000000..8e0ee81414faa --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/patch_list_items_migrations.ts @@ -0,0 +1,95 @@ +/* + * Copyright 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 type { PatchListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { LIST_ITEM_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; + +import { + createListsIndex, + deleteListsIndex, + createListsIndices, + createListBypassingChecks, + createListItemBypassingChecks, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + + describe('@ess patch_list_items_migrations', () => { + describe('patch list items', () => { + beforeEach(async () => { + await createListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + + describe('legacy list items index (list created before migration to data stream)', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + it('should patch list item that was created in legacy index and migrated through LIST_INDEX request', async () => { + const listId = 'random-list'; + const listItemId = 'random-list-item'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); + // migrates old indices to data streams + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); + + const patchPayload: PatchListItemSchema = { + id: listItemId, + value: 'new one', + }; + + const { body } = await supertest + .patch(LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(patchPayload) + .expect(200); + + expect(body.value).to.be('new one'); + }); + + it('should patch list item that was created in legacy index and not yet migrated', async () => { + const listId = 'random-list'; + const listItemId = 'random-list-item'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); + + const patchPayload: PatchListItemSchema = { + id: listItemId, + value: 'new one', + }; + + const { body } = await supertest + .patch(LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(patchPayload) + .expect(200); + + expect(body.value).to.be('new one'); + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/read_list_items.ts similarity index 85% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/read_list_items.ts index dc99e9d4d180a..b0005ccb3fc0b 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/read_list_items.ts @@ -11,20 +11,22 @@ import { LIST_URL, LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('read_list_items', () => { + describe('@ess @serverless read_list_items', () => { describe('reading list items', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -53,7 +55,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single list item with an auto-generated list id', async () => { @@ -75,7 +79,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListItemServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListItemResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should return 404 if given a fake id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items.ts similarity index 80% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/update_list_items.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items.ts index e2bcddeb24841..90d246d141866 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items.ts @@ -12,30 +12,27 @@ import type { CreateListItemSchema, ListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import { LIST_URL, LIST_ITEM_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; +import { LIST_URL, LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; import { getListItemResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_item_schema.mock'; import { getCreateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_item_schema.mock'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getUpdateMinimalListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_list_item_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListItemServerGeneratedProperties, - createListsIndices, - createListBypassingChecks, - createListItemBypassingChecks, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); const retry = getService('retry'); - const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('update_list_items', () => { + describe('@ess @serverless update_list_items', () => { describe('update list items', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -72,7 +69,7 @@ export default ({ getService }: FtrProviderContext) => { .send(updatedListItem); const outputListItem: Partial = { - ...getListItemResponseMockWithoutAutoGeneratedValues(), + ...getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), value: '192.168.0.2', }; const bodyToCompare = removeListItemServerGeneratedProperties(body); @@ -122,7 +119,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputListItem: Partial = { - ...getListItemResponseMockWithoutAutoGeneratedValues(), + ...getListItemResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), value: '192.168.0.2', }; const bodyToCompare = { @@ -305,63 +302,6 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); }); }); - - describe('legacy list items index (list created before migration to data stream)', () => { - beforeEach(async () => { - await deleteListsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteListsIndex(supertest, log); - }); - it('should update list item that was created in legacy index and migrated through LIST_INDEX request', async () => { - const listId = 'random-list'; - const listItemId = 'random-list-item'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); - // migrates old indices to data streams - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); - - const updatedListItem: UpdateListItemSchema = { - id: listItemId, - value: 'new one', - }; - - const { body } = await supertest - .put(LIST_ITEM_URL) - .set('kbn-xsrf', 'true') - .send(updatedListItem) - .expect(200); - - expect(body.value).to.be('new one'); - }); - - it('should update list item that was created in legacy index and not yet migrated', async () => { - const listId = 'random-list'; - const listItemId = 'random-list-item'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); - - const updatedListItem: UpdateListItemSchema = { - id: listItemId, - value: 'new one', - }; - - const { body } = await supertest - .put(LIST_ITEM_URL) - .set('kbn-xsrf', 'true') - .send(updatedListItem) - .expect(200); - - expect(body.value).to.be('new one'); - }); - }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items_migrations.ts new file mode 100644 index 0000000000000..06296ed60589e --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/items/update_list_items_migrations.ts @@ -0,0 +1,95 @@ +/* + * Copyright 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 type { UpdateListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { LIST_ITEM_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; + +import { + createListsIndex, + deleteListsIndex, + createListsIndices, + createListBypassingChecks, + createListItemBypassingChecks, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + + describe('@ess update_list_items_migrations', () => { + describe('update list items', () => { + beforeEach(async () => { + await createListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + + describe('legacy list items index (list created before migration to data stream)', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + it('should update list item that was created in legacy index and migrated through LIST_INDEX request', async () => { + const listId = 'random-list'; + const listItemId = 'random-list-item'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); + // migrates old indices to data streams + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); + + const updatedListItem: UpdateListItemSchema = { + id: listItemId, + value: 'new one', + }; + + const { body } = await supertest + .put(LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(updatedListItem) + .expect(200); + + expect(body.value).to.be('new one'); + }); + + it('should update list item that was created in legacy index and not yet migrated', async () => { + const listId = 'random-list'; + const listItemId = 'random-list-item'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + await createListItemBypassingChecks({ es, listId, id: listItemId, value: 'random' }); + + const updatedListItem: UpdateListItemSchema = { + id: listItemId, + value: 'new one', + }; + + const { body } = await supertest + .put(LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(updatedListItem) + .expect(200); + + expect(body.value).to.be('new one'); + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists.ts similarity index 81% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists.ts index b64f9d4206db3..8cd7517c9efe8 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists.ts @@ -13,20 +13,22 @@ import { getCreateMinimalListSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('create_lists', () => { + describe('@ess @serverless create_lists', () => { describe('creating lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -44,7 +46,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should create a simple list without a list_id', async () => { @@ -55,7 +59,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should cause a 409 conflict if we attempt to create the same list_id twice', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index.ts new file mode 100644 index 0000000000000..bb9167f4ba8a2 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { LIST_INDEX } from '@kbn/securitysolution-list-constants'; + +import { deleteListsIndex } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + + describe('@ess @serverless create_list_index_route', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + + it('should create lists data streams', async () => { + const { body: fetchedIndices } = await supertest + .get(LIST_INDEX) + .set('kbn-xsrf', 'true') + .expect(404); + + expect(fetchedIndices).to.eql({ + message: 'data stream .lists-default and data stream .items-default does not exist', + status_code: 404, + }); + + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').expect(200); + + const { body } = await supertest.get(LIST_INDEX).set('kbn-xsrf', 'true').expect(200); + + expect(body).to.eql({ list_index: true, list_item_index: true }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists_index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index_migrations.ts similarity index 77% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists_index.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index_migrations.ts index ab549f27c4d2d..4cd8b1afd74a0 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_lists_index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/create_lists_index_migrations.ts @@ -9,17 +9,17 @@ import expect from '@kbn/expect'; import { LIST_INDEX } from '@kbn/securitysolution-list-constants'; import { getTemplateExists, getIndexTemplateExists } from '@kbn/securitysolution-es-utils'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createLegacyListsIndices, deleteListsIndex } from '../../utils'; +import { createLegacyListsIndices, deleteListsIndex } from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); const es = getService('es'); - describe('create_list_index_route', () => { + describe('@ess create_list_index_route_migrations', () => { beforeEach(async () => { await deleteListsIndex(supertest, log); }); @@ -28,24 +28,6 @@ export default ({ getService }: FtrProviderContext) => { await deleteListsIndex(supertest, log); }); - it('should create lists data streams', async () => { - const { body: fetchedIndices } = await supertest - .get(LIST_INDEX) - .set('kbn-xsrf', 'true') - .expect(404); - - expect(fetchedIndices).to.eql({ - message: 'data stream .lists-default and data stream .items-default does not exist', - status_code: 404, - }); - - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').expect(200); - - const { body } = await supertest.get(LIST_INDEX).set('kbn-xsrf', 'true').expect(200); - - expect(body).to.eql({ list_index: true, list_item_index: true }); - }); - it('should migrate lists indices to data streams and remove old legacy templates', async () => { // create legacy indices await createLegacyListsIndices(es); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/delete_lists.ts similarity index 92% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/delete_lists.ts index 1e9b4911c3092..87b54d9a2e99a 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/delete_lists.ts @@ -27,15 +27,16 @@ import { deleteAllExceptions, deleteListsIndex, removeListServerGeneratedProperties, -} from '../../utils'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('delete_lists', () => { + describe('@ess @serverless delete_lists', () => { describe('deleting lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -60,7 +61,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should delete a single list with a list id containing non-alphanumeric characters', async () => { @@ -82,7 +85,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should delete a single list using an auto generated id', async () => { @@ -100,7 +105,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should return an error if the id does not exist when trying to delete it', async () => { @@ -252,7 +259,9 @@ export default ({ getService }: FtrProviderContext) => { .set('kbn-xsrf', 'true'); const bodyToCompare = removeListServerGeneratedProperties(deleteListBody.body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); await supertest .get(`${LIST_ITEM_URL}/_find?list_id=${LIST_ID}`) diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists.ts similarity index 86% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists.ts index ffb13b66c8db9..3c47ad92eb824 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists.ts @@ -10,20 +10,22 @@ import expect from '@kbn/expect'; import { LIST_URL } from '@kbn/securitysolution-list-constants'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('find_lists', () => { + describe('@ess @serverless find_lists', () => { describe('find lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -68,7 +70,7 @@ export default ({ getService }: FtrProviderContext): void => { // cursor is a constant changing value so we have to delete it as well. delete body.cursor; expect(body).to.eql({ - data: [getListResponseMockWithoutAutoGeneratedValues()], + data: [getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME)], page: 1, per_page: 20, total: 1, diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists_by_size.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists_by_size.ts similarity index 79% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists_by_size.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists_by_size.ts index 0d348f5c63424..813293ed1e7cc 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_lists_by_size.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/find_lists_by_size.ts @@ -6,24 +6,29 @@ */ import expect from '@kbn/expect'; - +import { + ELASTIC_HTTP_VERSION_HEADER, + X_ELASTIC_INTERNAL_ORIGIN_REQUEST, +} from '@kbn/core-http-common'; import { LIST_URL, INTERNAL_FIND_LISTS_BY_SIZE } from '@kbn/securitysolution-list-constants'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('find_lists_by_size', () => { + describe('@ess @serverless find_lists_by_size', () => { describe('find lists by size', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -37,7 +42,8 @@ export default ({ getService }: FtrProviderContext): void => { const { body } = await supertest .get(`${INTERNAL_FIND_LISTS_BY_SIZE}`) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .send() .expect(200); @@ -66,7 +72,8 @@ export default ({ getService }: FtrProviderContext): void => { const { body } = await supertest .get(`${INTERNAL_FIND_LISTS_BY_SIZE}`) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .send() .expect(200); @@ -75,10 +82,10 @@ export default ({ getService }: FtrProviderContext): void => { // cursor is a constant changing value so we have to delete it as well. delete body.cursor; expect(body).to.eql({ - smallLists: [getListResponseMockWithoutAutoGeneratedValues()], + smallLists: [getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME)], largeLists: [ { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), type: 'text', }, ], diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/index.ts new file mode 100644 index 0000000000000..c661171dfedbe --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/index.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 { FtrProviderContext } from '../../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Lists API', function () { + loadTestFile(require.resolve('./create_lists')); + loadTestFile(require.resolve('./create_lists_index')); + loadTestFile(require.resolve('./create_lists_index_migrations')); + loadTestFile(require.resolve('./patch_lists')); + loadTestFile(require.resolve('./patch_lists_migrations')); + loadTestFile(require.resolve('./read_lists')); + loadTestFile(require.resolve('./update_lists')); + loadTestFile(require.resolve('./update_lists_migrations')); + loadTestFile(require.resolve('./delete_lists')); + loadTestFile(require.resolve('./find_lists')); + loadTestFile(require.resolve('./find_lists_by_size')); + }); +} diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/patch_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists.ts similarity index 73% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/patch_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists.ts index 87076851bd34c..2586dcb23ab4f 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/patch_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import type { PatchListSchema, ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { LIST_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; +import { LIST_URL } from '@kbn/securitysolution-list-constants'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; @@ -17,19 +17,17 @@ import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, - createListsIndices, - createListBypassingChecks, -} from '../../utils'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); const retry = getService('retry'); - const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('patch_lists', () => { + describe('@ess @serverless patch_lists', () => { describe('patch lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -61,7 +59,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -102,7 +100,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -142,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -212,60 +210,6 @@ export default ({ getService }: FtrProviderContext) => { message: 'list id: "5096dec6-b6b9-4d8d-8f93-6c2602079d9d" not found', }); }); - - describe('legacy list index (list created before migration to data stream)', () => { - beforeEach(async () => { - await deleteListsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteListsIndex(supertest, log); - }); - it('should update list container that was created in legacy index and migrated through LIST_INDEX request', async () => { - const listId = 'random-list'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - - // migrates old indices to data streams - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); - - // patch a simple list's name - const patchPayload: PatchListSchema = { - id: listId, - name: 'some other name', - }; - const { body } = await supertest - .patch(LIST_URL) - .set('kbn-xsrf', 'true') - .send(patchPayload) - .expect(200); - - expect(body.name).to.be('some other name'); - }); - - it('should update list container that was created in legacy index and not yet migrated', async () => { - const listId = 'random-list'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - - // patch a simple list's name - const patchPayload: PatchListSchema = { - id: listId, - name: 'some other name', - }; - const { body } = await supertest - .patch(LIST_URL) - .set('kbn-xsrf', 'true') - .send(patchPayload) - .expect(200); - - expect(body.name).to.be('some other name'); - }); - }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists_migrations.ts new file mode 100644 index 0000000000000..d131dc4ba05bd --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/patch_lists_migrations.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import type { PatchListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { LIST_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; + +import { + createListsIndex, + deleteListsIndex, + createListsIndices, + createListBypassingChecks, +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + + describe('@ess patch_lists_migrations', () => { + describe('patch lists', () => { + beforeEach(async () => { + await createListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + describe('legacy list index (list created before migration to data stream)', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + it('should update list container that was created in legacy index and migrated through LIST_INDEX request', async () => { + const listId = 'random-list'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + + // migrates old indices to data streams + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); + + // patch a simple list's name + const patchPayload: PatchListSchema = { + id: listId, + name: 'some other name', + }; + const { body } = await supertest + .patch(LIST_URL) + .set('kbn-xsrf', 'true') + .send(patchPayload) + .expect(200); + + expect(body.name).to.be('some other name'); + }); + + it('should update list container that was created in legacy index and not yet migrated', async () => { + const listId = 'random-list'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + + // patch a simple list's name + const patchPayload: PatchListSchema = { + id: listId, + name: 'some other name', + }; + const { body } = await supertest + .patch(LIST_URL) + .set('kbn-xsrf', 'true') + .send(patchPayload) + .expect(200); + + expect(body.name).to.be('some other name'); + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_privileges.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_list_privileges.ts similarity index 91% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_privileges.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_list_privileges.ts index 9af6143b2152f..85e4309d048a2 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_list_privileges.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_list_privileges.ts @@ -9,16 +9,15 @@ import expect from '@kbn/expect'; import { LIST_PRIVILEGES_URL } from '@kbn/securitysolution-list-constants'; import { getReadPrivilegeMock } from '@kbn/lists-plugin/server/routes/list_privileges/read_list_privileges_route.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContextWithSpaces } from '../../../../../ftr_provider_context_with_spaces'; -// eslint-disable-next-line import/no-default-export -export default ({ getService }: FtrProviderContext) => { +export default ({ getService }: FtrProviderContextWithSpaces) => { const supertest = getService('supertest'); const security = getService('security'); const spacesService = getService('spaces'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('read_list_privileges', () => { + describe('@ess @serverless read_list_privileges', () => { const space1Id = 'space_1'; const user1 = { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_lists.ts similarity index 83% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/read_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_lists.ts index 162b57501c479..025725fe01575 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/read_lists.ts @@ -13,20 +13,21 @@ import { getCreateMinimalListSchemaMockWithoutId, } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, -} from '../../utils'; +} from '../../../utils'; +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('read_lists', () => { + describe('@ess @serverless read_lists', () => { describe('reading lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -50,7 +51,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should be able to read a single list with an auto-generated list id', async () => { @@ -67,7 +70,9 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const bodyToCompare = removeListServerGeneratedProperties(body); - expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + expect(bodyToCompare).to.eql( + getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME) + ); }); it('should return 404 if given a fake id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists.ts similarity index 79% rename from x-pack/test/lists_api_integration/security_and_spaces/tests/update_lists.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists.ts index d9fc0bbe38bd3..28084f54d2abe 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import type { UpdateListSchema, ListSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { LIST_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; +import { LIST_URL } from '@kbn/securitysolution-list-constants'; import { getCreateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_list_schema.mock'; import { getListResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/list_schema.mock'; @@ -17,19 +17,18 @@ import { createListsIndex, deleteListsIndex, removeListServerGeneratedProperties, - createListsIndices, - createListBypassingChecks, -} from '../../utils'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); const retry = getService('retry'); - const es = getService('es'); + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); - describe('update_lists', () => { + describe('@ess @serverless update_lists', () => { describe('update lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); @@ -60,7 +59,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -90,7 +89,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', version: 2, }; @@ -189,7 +188,7 @@ export default ({ getService }: FtrProviderContext) => { const { body } = await supertest.put(LIST_URL).set('kbn-xsrf', 'true').send(updatedList); const outputList: Partial = { - ...getListResponseMockWithoutAutoGeneratedValues(), + ...getListResponseMockWithoutAutoGeneratedValues(ELASTICSEARCH_USERNAME), name: 'some other name', description: 'some other description', version: 2, @@ -280,61 +279,6 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); }); }); - - describe('legacy list index (list created before migration to data stream)', () => { - beforeEach(async () => { - await deleteListsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteListsIndex(supertest, log); - }); - it('should update list container that was created in legacy index and migrated through LIST_INDEX request', async () => { - const listId = 'random-list'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - - // migrates old indices to data streams - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); - - // update a simple list's name - const updatedList: UpdateListSchema = { - ...getUpdateMinimalListSchemaMock(), - id: listId, - name: 'some other name', - }; - const { body } = await supertest - .put(LIST_URL) - .set('kbn-xsrf', 'true') - .send(updatedList) - .expect(200); - - expect(body.name).to.be('some other name'); - }); - - it('should update list container that was created in legacy index and not yet migrated', async () => { - const listId = 'random-list'; - // create legacy indices - await createListsIndices(es); - // create a simple list - await createListBypassingChecks({ es, id: listId }); - - // update a simple list's name - const updatedList: UpdateListSchema = { - ...getUpdateMinimalListSchemaMock(), - id: listId, - name: 'some other name', - }; - const { body } = await supertest - .put(LIST_URL) - .set('kbn-xsrf', 'true') - .send(updatedList) - .expect(200); - expect(body.name).to.be('some other name'); - }); - }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists_migrations.ts new file mode 100644 index 0000000000000..3acffe061f2cd --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/lists/update_lists_migrations.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import type { UpdateListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { LIST_URL, LIST_INDEX } from '@kbn/securitysolution-list-constants'; + +import { getUpdateMinimalListSchemaMock } from '@kbn/lists-plugin/common/schemas/request/update_list_schema.mock'; +import { + createListsIndex, + deleteListsIndex, + createListsIndices, + createListBypassingChecks, +} from '../../../utils'; + +import { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + const es = getService('es'); + + describe('@ess update_lists_migrations', () => { + describe('update lists', () => { + beforeEach(async () => { + await createListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + + describe('legacy list index (list created before migration to data stream)', () => { + beforeEach(async () => { + await deleteListsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteListsIndex(supertest, log); + }); + it('should update list container that was created in legacy index and migrated through LIST_INDEX request', async () => { + const listId = 'random-list'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + + // migrates old indices to data streams + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true'); + + // update a simple list's name + const updatedList: UpdateListSchema = { + ...getUpdateMinimalListSchemaMock(), + id: listId, + name: 'some other name', + }; + const { body } = await supertest + .put(LIST_URL) + .set('kbn-xsrf', 'true') + .send(updatedList) + .expect(200); + + expect(body.name).to.be('some other name'); + }); + + it('should update list container that was created in legacy index and not yet migrated', async () => { + const listId = 'random-list'; + // create legacy indices + await createListsIndices(es); + // create a simple list + await createListBypassingChecks({ es, id: listId }); + + // update a simple list's name + const updatedList: UpdateListSchema = { + ...getUpdateMinimalListSchemaMock(), + id: listId, + name: 'some other name', + }; + const { body } = await supertest + .put(LIST_URL) + .set('kbn-xsrf', 'true') + .send(updatedList) + .expect(200); + expect(body.name).to.be('some other name'); + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/utils.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts similarity index 99% rename from x-pack/test/lists_api_integration/utils.ts rename to x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts index 780042a293dcc..be05bb5a47518 100644 --- a/x-pack/test/lists_api_integration/utils.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts @@ -33,7 +33,7 @@ import { ToolingLog } from '@kbn/tooling-log'; import { getImportListItemAsBuffer } from '@kbn/lists-plugin/common/schemas/request/import_list_item_schema.mock'; import { encodeHitVersion } from '@kbn/securitysolution-es-utils'; -import { countDownTest } from '../detection_engine_api_integration/utils'; +import { countDownTest } from '../detections_response/utils'; /** * Creates the lists and lists items index for use inside of beforeEach blocks of tests diff --git a/x-pack/test/security_solution_api_integration/tsconfig.json b/x-pack/test/security_solution_api_integration/tsconfig.json index 782bc4fd46b32..6b1ca69f6aed1 100644 --- a/x-pack/test/security_solution_api_integration/tsconfig.json +++ b/x-pack/test/security_solution_api_integration/tsconfig.json @@ -36,5 +36,6 @@ "@kbn/securitysolution-ecs", "@kbn/fleet-plugin", "@kbn/repo-info", + "@kbn/securitysolution-es-utils", ] } diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 99390aa5142a2..fb34362f7fb9b 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -46,7 +46,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true', `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'chartEmbeddablesEnabled', - 'disableTimelineSaveTour', ])}`, // mock cloud to enable the guided onboarding tour in e2e tests '--xpack.cloud.id=test', diff --git a/x-pack/test/security_solution_cypress/cypress/README.md b/x-pack/test/security_solution_cypress/cypress/README.md index 8940d6c86e73e..88786aed7ff56 100644 --- a/x-pack/test/security_solution_cypress/cypress/README.md +++ b/x-pack/test/security_solution_cypress/cypress/README.md @@ -62,19 +62,25 @@ Run the tests with the following yarn scripts from `x-pack/test/security_solutio | cypress | Runs the default Cypress command | | cypress:open:ess | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a local kibana and ES instance. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. | | cypress:open:serverless | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a mocked serverless environment. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. | -| cypress:run:ess | Runs all tests tagged as ESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode | +| cypress:run:ess | Runs all tests tagged as ESS placed in the `e2e` directory excluding `investigations`,`explore` and `detection_response/rule_management` directories in headless mode | | cypress:run:cases:ess | Runs all tests under `explore/cases` in the `e2e` directory related to the Cases area team in headless mode | | cypress:ess | Runs all ESS tests with the specified configuration in headless mode and produces a report using `cypress-multi-reporters` | +| cypress:rule_management:run:ess | Runs all tests tagged as ESS in the `e2e/detection_response/rule_management` excluding `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | +| cypress:rule_management:prebuilt_rules:run:ess | Runs all tests tagged as ESS in the `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | | cypress:run:respops:ess | Runs all tests related to the Response Ops area team, specifically tests in `detection_alerts`, `detection_rules`, and `exceptions` directories in headless mode | -| cypress:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e` directory excluding `investigations` and `explore` directories in headless mode | -| cypress:investigations:run:ess | Runs all tests tagged as ESS in the `e2e/investigations` directory in headless mode | +| cypress:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e` directory excluding `investigations`, `explore` and `rule_management` directories in headless mode | +| cypress:rule_management:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management` excluding `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | +| cypress:rule_management:prebuilt_rules:run:serverless | Runs all tests tagged as ESS in the `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | +| cypress:investigations:run:ess | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode | | cypress:explore:run:ess | Runs all tests tagged as ESS in the `e2e/explore` directory in headless mode | | cypress:investigations:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode | | cypress:explore:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode | | cypress:open:qa:serverless | Opens the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment. The kibana instance will reload when you make code changes. This is the recommended way to debug tests in QA. Follow the readme in order to learn about the known limitations. | -| cypress:run:qa:serverless | Runs all tests tagged as SERVERLESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode using the QA environment and real MKI projects.| +| cypress:run:qa:serverless | Runs all tests tagged as SERVERLESS placed in the `e2e` directory excluding `investigations`, `explore` and `rule_management` directories in headless mode using the QA environment and real MKI projects.| | cypress:run:qa:serverless:explore | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode using the QA environment and real MKI prorjects. | | cypress:run:qa:serverless:investigations | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode using the QA environment and reak MKI projects. | +| cypress:run:qa:serverless:rule_management | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management` directory, excluding `e2e/detection_response/rule_management/prebuilt_rules` in headless mode using the QA environment and reak MKI projects. | +| cypress:run:qa:serverless:rule_management:prebuilt_rules | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode using the QA environment and reak MKI projects. | | junit:merge | Merges individual test reports into a single report and moves the report to the `junit` directory | Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging. @@ -94,7 +100,7 @@ Below you can find the folder structure used on our Cypress tests. Cypress convention starting version 10 (previously known as integration). Contains the specs that are going to be executed. -### e2e/explore and e2e/investigations +### Area teams folders These directories contain tests which are run in their own Buildkite pipeline. @@ -103,7 +109,8 @@ If you belong to one of the teams listed in the table, please add new e2e specs | Directory | Area team | | -- | -- | | `e2e/explore` | Threat Hunting Explore | -| `e2e/investigations | Threat Hunting Investigations | +| `e2e/investigations` | Threat Hunting Investigations | +| `e2e/detection_response/rule_management` | Detection Rule Management | ### fixtures/ @@ -203,6 +210,8 @@ Run the tests with the following yarn scripts from `x-pack/test/security_solutio | cypress:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e` directory excluding `investigations` and `explore` directories in headless mode | | cypress:investigations:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode | | cypress:explore:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode | +| cypress:rule_management:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management` excluding `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | +| cypress:rule_management:prebuilt_rules:run:serverless | Runs all tests tagged as ESS in the `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode | Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging. @@ -248,6 +257,8 @@ Run the tests with the following yarn scripts from `x-pack/test/security_solutio | cypress:run:qa:serverless | Runs all tests tagged as SERVERLESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode using the QA environment and real MKI projects.| | cypress:run:qa:serverless:explore | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode using the QA environment and real MKI prorjects. | | cypress:run:qa:serverless:investigations | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode using the QA environment and reak MKI projects. | +| cypress:run:qa:serverless:rule_management | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management` directory, excluding `e2e/detection_response/rule_management/prebuilt_rules` in headless mode using the QA environment and reak MKI projects. | +| cypress:run:qa:serverless:rule_management:prebuilt_rules | Runs all tests tagged as SERVERLESS in the `e2e/detection_response/rule_management/prebuilt_rules` directory in headless mode using the QA environment and reak MKI projects. | Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging. diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_status.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_status.cy.ts index e7f17ddcc8cb2..1b03577e778ba 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_status.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_status.cy.ts @@ -30,7 +30,7 @@ import { openFirstAlert, } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; @@ -247,7 +247,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@serverless'] }, () => deleteAlertsAndRules(); createRule(getNewRule()); login(ROLES.reader); - visit(ALERTS_URL, { role: ROLES.reader }); + visit(ALERTS_URL); waitForAlertsToPopulate(); }); it('should not allow users to change a single alert status', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts index 162c63ad3ce48..4fb4d50e7c6d9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts @@ -13,7 +13,7 @@ import { updateAlertTags, } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { login } from '../../../tasks/login'; import { visitWithTimeRange } from '../../../tasks/navigation'; import { ALERTS_URL } from '../../../urls/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts index bbdba453351bb..8ffb094f94e5a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts @@ -25,7 +25,7 @@ import { const loadPageAsPlatformEngineerUser = (url: string) => { login(ROLES.soc_manager); - visit(url, { role: ROLES.soc_manager }); + visit(url); waitForPageTitleToBeShown(); }; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/missing_privileges_callout.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/missing_privileges_callout.cy.ts index 3e75f8b4a28bb..4dffc5f322ad8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/missing_privileges_callout.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/missing_privileges_callout.cy.ts @@ -25,13 +25,13 @@ import { ruleDetailsUrl } from '../../../urls/rule_details'; const loadPageAsReadOnlyUser = (url: string) => { login(ROLES.t1_analyst); - visit(url, { role: ROLES.t1_analyst }); + visit(url); waitForPageTitleToBeShown(); }; const loadPageAsPlatformEngineer = (url: string) => { login(ROLES.platform_engineer); - visit(url, { role: ROLES.platform_engineer }); + visit(url); waitForPageTitleToBeShown(); }; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions.cy.ts index 98080cb3b47e8..3053f8e5c5698 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions.cy.ts @@ -10,7 +10,11 @@ import { getSimpleCustomQueryRule } from '../../../objects/rule'; import { goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; import { deleteIndex, waitForNewDocumentToBeIndexed } from '../../../tasks/api_calls/elasticsearch'; -import { deleteAlertsAndRules, deleteConnectors, deleteDataView } from '../../../tasks/common'; +import { + deleteAlertsAndRules, + deleteConnectors, + deleteDataView, +} from '../../../tasks/api_calls/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts index 23421b218bcb6..13c35a3cce6c4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts @@ -18,7 +18,7 @@ import { import { createRule } from '../../../tasks/api_calls/rules'; import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { goToActionsStepTab } from '../../../tasks/create_new_rule'; import { login } from '../../../tasks/login'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts index 83503ea98738d..d36cdc7137de6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts @@ -18,7 +18,7 @@ import { import { createRule } from '../../../tasks/api_calls/rules'; import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { goToActionsStepTab } from '../../../tasks/create_new_rule'; import { login } from '../../../tasks/login'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/common_flows.cy.ts index e8780d8696d29..9628f03f2d102 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/common_flows.cy.ts @@ -19,7 +19,7 @@ import { } from '../../../screens/create_new_rule'; import { RULE_NAME_HEADER } from '../../../screens/rule_details'; import { createTimeline } from '../../../tasks/api_calls/timelines'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { createAndEnableRule, expandAdvancedSettings, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts index e4bb4b2bfba83..5e41440f48f4e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule.cy.ts @@ -8,7 +8,7 @@ import { getNewRule } from '../../../objects/rule'; import { RULE_NAME_HEADER } from '../../../screens/rule_details'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { fillScheduleRuleAndContinue, fillAboutRuleMinimumAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts index d13a676e84253..7a6d1fa889e58 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts @@ -52,7 +52,11 @@ import { getRulesManagementTableRows, goToRuleDetailsOf, } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules, deleteDataView, postDataView } from '../../../tasks/common'; +import { + deleteAlertsAndRules, + deleteDataView, + postDataView, +} from '../../../tasks/api_calls/common'; import { createAndEnableRule, createRuleWithoutEnabling, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_saved_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_saved_query_rule.cy.ts index 03ba3db7f25ff..f55a51d8e4f64 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_saved_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_saved_query_rule.cy.ts @@ -24,7 +24,7 @@ import { import { editFirstRule, goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; import { createSavedQuery, deleteSavedQueries } from '../../../tasks/api_calls/saved_queries'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/esql_rule_ess.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/esql_rule_ess.cy.ts index 2fa97e4f76e40..f3a898e430a4e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/esql_rule_ess.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/esql_rule_ess.cy.ts @@ -18,7 +18,7 @@ import { ESQL_TYPE, ESQL_QUERY_BAR } from '../../../screens/create_new_rule'; import { getDetails, goBackToRulesTable } from '../../../tasks/rule_details'; import { expectNumberOfRules } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { fillAboutRuleAndContinue, fillDefineEsqlRuleAndContinue, @@ -38,7 +38,8 @@ describe('Detection ES|QL rules, creation', { tags: ['@ess'] }, () => { const rule = getEsqlRule(); const expectedNumberOfRules = 1; - describe('creation', () => { + // FLAKY: https://github.com/elastic/kibana/issues/172251 + describe.skip('creation', () => { beforeEach(() => { deleteAlertsAndRules(); login(); @@ -85,7 +86,8 @@ describe('Detection ES|QL rules, creation', { tags: ['@ess'] }, () => { }); }); - describe('ES|QL query validation', () => { + // FLAKY: https://github.com/elastic/kibana/issues/172074 + describe.skip('ES|QL query validation', () => { beforeEach(() => { login(); visit(CREATE_RULE_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts index b895460661858..0966ae2709113 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/event_correlation_rule.cy.ts @@ -43,7 +43,7 @@ import { import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts index 6dbe81076c91e..2b83c938b9473 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/indicator_match_rule.cy.ts @@ -111,7 +111,7 @@ import { import { CREATE_RULE_URL } from '../../../urls/navigation'; import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; import { openRuleManagementPageViaBreadcrumbs } from '../../../tasks/rules_management'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/new_terms_rule.cy.ts index 8b8c6fe4e1457..570f19f3f72e1 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/new_terms_rule.cy.ts @@ -45,7 +45,7 @@ import { import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/override.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/override.cy.ts index 585cd9187f3e0..9bb9f569c4dd1 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/override.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/override.cy.ts @@ -47,7 +47,7 @@ import { TIMESTAMP_OVERRIDE_DETAILS, } from '../../../screens/rule_details'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { expectNumberOfRules, goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; import { createAndEnableRule, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts index 503b40f568303..62fdb9121c9c5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/threshold_rule.cy.ts @@ -45,7 +45,7 @@ import { import { getDetails, waitForTheRuleToBeExecuted } from '../../../tasks/rule_details'; import { expectNumberOfRules, goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { createAndEnableRule, fillAboutRuleAndContinue, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/custom_query_rule.cy.ts index ca6d6c56adcf7..e9497851d4cb0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/custom_query_rule.cy.ts @@ -42,7 +42,7 @@ import { } from '../../../screens/rule_details'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common'; +import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/api_calls/common'; import { addEmailConnectorAndRuleAction } from '../../../tasks/common/rule_actions'; import { fillAboutRule, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/esql_rule.cy.ts index eb16d89a6af8c..445b33a682f9c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/esql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_edit/esql_rule.cy.ts @@ -15,7 +15,7 @@ import { createRule } from '../../../tasks/api_calls/rules'; import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; import { getDetails } from '../../../tasks/rule_details'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { clearEsqlQueryBar, fillEsqlQueryBar, @@ -34,7 +34,10 @@ const rule = getEsqlRule(); const expectedValidEsqlQuery = 'from auditbeat* | stats count(event.category) by event.category'; -describe('Detection ES|QL rules, edit', { tags: ['@ess'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/172253 +// FLAKY: https://github.com/elastic/kibana/issues/172254 +// FLAKY: https://github.com/elastic/kibana/issues/172255 +describe.skip('Detection ES|QL rules, edit', { tags: ['@ess'] }, () => { beforeEach(() => { login(); deleteAlertsAndRules(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts index 244637126ecc2..f89a01dff1bed 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts @@ -33,7 +33,7 @@ describe('All rules - read only', { tags: ['@ess', '@serverless', '@skipInServer beforeEach(() => { login(ROLES.t1_analyst); - visitRulesManagementTable(ROLES.t1_analyst); + visitRulesManagementTable(); cy.get(RULE_NAME).should('have.text', getNewRule().name); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_authorization.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_authorization.cy.ts similarity index 93% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_authorization.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_authorization.cy.ts index e0078dd54e7ea..a60d6d6fe56d2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_authorization.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_authorization.cy.ts @@ -12,14 +12,14 @@ import { } from '@kbn/security-solution-plugin/common/constants'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { createAndInstallMockedPrebuiltRules, installPrebuiltRuleAssets, preventPrebuiltRulesPackageInstallation, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { visit } from '../../../tasks/navigation'; -import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { visit } from '../../../../tasks/navigation'; +import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; import { ADD_ELASTIC_RULES_BTN, getInstallSingleRuleButtonByRuleId, @@ -31,8 +31,8 @@ import { RULES_UPDATES_TAB, RULE_CHECKBOX, UPGRADE_ALL_RULES_BUTTON, -} from '../../../screens/alerts_detection_rules'; -import { login } from '../../../tasks/login'; +} from '../../../../screens/alerts_detection_rules'; +import { login } from '../../../../tasks/login'; // Rule to test update const RULE_1_ID = 'rule_1'; @@ -57,12 +57,12 @@ const RULE_2 = createRuleAssetSavedObject({ const loadPageAsReadOnlyUser = (url: string) => { login(ROLES.t1_analyst); - visit(url, { role: ROLES.t1_analyst }); + visit(url); }; const loginPageAsWriteAuthorizedUser = (url: string) => { login(ROLES.t3_analyst); - visit(url, { role: ROLES.t3_analyst }); + visit(url); }; describe( diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_error_handling.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_error_handling.cy.ts similarity index 94% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_error_handling.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_error_handling.cy.ts index 7e288910ccb60..db84d92e4ddb6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_error_handling.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_update_error_handling.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { getInstallSingleRuleButtonByRuleId, getUpgradeSingleRuleButtonByRuleId, @@ -14,14 +14,14 @@ import { SELECT_ALL_RULES_ON_PAGE_CHECKBOX, UPGRADE_ALL_RULES_BUTTON, UPGRADE_SELECTED_RULES_BUTTON, -} from '../../../screens/alerts_detection_rules'; -import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; +} from '../../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../../tasks/alerts_detection_rules'; import { installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, preventPrebuiltRulesPackageInstallation, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { login } from '../../../tasks/login'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { login } from '../../../../tasks/login'; import { clickAddElasticRulesButton, assertInstallationRequestIsComplete, @@ -33,8 +33,8 @@ import { assertRulesPresentInAddPrebuiltRulesTable, assertRuleUpgradeFailureToastShown, assertRulesPresentInRuleUpdatesTable, -} from '../../../tasks/prebuilt_rules'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +} from '../../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; describe( 'Detection rules, Prebuilt Rules Installation and Update - Error handling', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_via_fleet.cy.ts similarity index 90% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_via_fleet.cy.ts index 630bd099a1d0c..762e79bb27003 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_via_fleet.cy.ts @@ -8,12 +8,13 @@ import type { BulkInstallPackageInfo } from '@kbn/fleet-plugin/common'; import type { Rule } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; -import { INSTALL_ALL_RULES_BUTTON, TOASTER } from '../../../screens/alerts_detection_rules'; -import { getRuleAssets } from '../../../tasks/api_calls/prebuilt_rules'; -import { login } from '../../../tasks/login'; -import { clickAddElasticRulesButton } from '../../../tasks/prebuilt_rules'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +import { resetRulesTableState } from '../../../../tasks/common'; +import { INSTALL_ALL_RULES_BUTTON, TOASTER } from '../../../../screens/alerts_detection_rules'; +import { getRuleAssets } from '../../../../tasks/api_calls/prebuilt_rules'; +import { login } from '../../../../tasks/login'; +import { clickAddElasticRulesButton } from '../../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; describe( 'Detection rules, Prebuilt Rules Installation and Update workflow', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_workflow.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_workflow.cy.ts similarity index 85% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_workflow.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_workflow.cy.ts index 15e77fad28d03..523d0ec0ad4e0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_workflow.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/install_workflow.cy.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { resetRulesTableState } from '../../../../tasks/common'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { getInstallSingleRuleButtonByRuleId, GO_BACK_TO_RULES_TABLE_BUTTON, @@ -16,18 +16,19 @@ import { RULE_CHECKBOX, SELECT_ALL_RULES_ON_PAGE_CHECKBOX, TOASTER, -} from '../../../screens/alerts_detection_rules'; -import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; -import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../../../screens/breadcrumbs'; -import { installPrebuiltRuleAssets } from '../../../tasks/api_calls/prebuilt_rules'; -import { login } from '../../../tasks/login'; +} from '../../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../../tasks/alerts_detection_rules'; +import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../../../../screens/breadcrumbs'; +import { installPrebuiltRuleAssets } from '../../../../tasks/api_calls/prebuilt_rules'; +import { login } from '../../../../tasks/login'; import { assertInstallationRequestIsComplete, assertRuleInstallationSuccessToastShown, assertRulesPresentInInstalledRulesTable, clickAddElasticRulesButton, -} from '../../../tasks/prebuilt_rules'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +} from '../../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; describe( 'Detection rules, Prebuilt Rules Installation and Update workflow', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/management.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/management.cy.ts similarity index 91% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/management.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/management.cy.ts index d5a3f3bb85326..15e020b5e0663 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/management.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/management.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { COLLAPSED_ACTION_BTN, ELASTIC_RULES_BTN, @@ -15,7 +15,7 @@ import { RULE_SWITCH, SELECT_ALL_RULES_ON_PAGE_CHECKBOX, INSTALL_ALL_RULES_BUTTON, -} from '../../../screens/alerts_detection_rules'; +} from '../../../../screens/alerts_detection_rules'; import { deleteFirstRule, disableAutoRefresh, @@ -24,21 +24,24 @@ import { selectRulesByName, waitForPrebuiltDetectionRulesToBeLoaded, waitForRuleToUpdate, -} from '../../../tasks/alerts_detection_rules'; +} from '../../../../tasks/alerts_detection_rules'; import { deleteSelectedRules, disableSelectedRules, enableSelectedRules, -} from '../../../tasks/rules_bulk_actions'; +} from '../../../../tasks/rules_bulk_actions'; import { createAndInstallMockedPrebuiltRules, getAvailablePrebuiltRulesCount, preventPrebuiltRulesPackageInstallation, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { deleteAlertsAndRules, deletePrebuiltRulesAssets } from '../../../tasks/common'; -import { login } from '../../../tasks/login'; -import { visit } from '../../../tasks/navigation'; -import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { + deleteAlertsAndRules, + deletePrebuiltRulesAssets, +} from '../../../../tasks/api_calls/common'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; const rules = Array.from(Array(5)).map((_, i) => { return createRuleAssetSavedObject({ diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/notifications.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/notifications.cy.ts similarity index 93% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/notifications.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/notifications.cy.ts index 180c435c10213..4812efc740ae2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/notifications.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/notifications.cy.ts @@ -5,25 +5,25 @@ * 2.0. */ -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { ADD_ELASTIC_RULES_BTN, ADD_ELASTIC_RULES_EMPTY_PROMPT_BTN, RULES_UPDATES_TAB, -} from '../../../screens/alerts_detection_rules'; -import { deleteFirstRule } from '../../../tasks/alerts_detection_rules'; +} from '../../../../screens/alerts_detection_rules'; +import { deleteFirstRule } from '../../../../tasks/alerts_detection_rules'; +import { + deleteAlertsAndRules, + deletePrebuiltRulesAssets, +} from '../../../../tasks/api_calls/common'; import { installAllPrebuiltRulesRequest, installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { - resetRulesTableState, - deleteAlertsAndRules, - deletePrebuiltRulesAssets, -} from '../../../tasks/common'; -import { login } from '../../../tasks/login'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { resetRulesTableState } from '../../../../tasks/common'; +import { login } from '../../../../tasks/login'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; const RULE_1 = createRuleAssetSavedObject({ name: 'Test rule 1', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_preview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts similarity index 97% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_preview.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts index 0b49fe4bb1dec..81f37b7760df2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_preview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts @@ -12,27 +12,22 @@ import type { PrebuiltRuleAsset } from '@kbn/security-solution-plugin/server/lib import type { Threshold } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema'; import { AlertSuppression } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema'; -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { INSTALL_PREBUILT_RULE_BUTTON, INSTALL_PREBUILT_RULE_PREVIEW, UPDATE_PREBUILT_RULE_PREVIEW, UPDATE_PREBUILT_RULE_BUTTON, -} from '../../../screens/alerts_detection_rules'; -import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../../../screens/breadcrumbs'; +} from '../../../../screens/alerts_detection_rules'; +import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../../../../screens/breadcrumbs'; import { installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { createSavedQuery, deleteSavedQueries } from '../../../tasks/api_calls/saved_queries'; -import { fetchMachineLearningModules } from '../../../tasks/api_calls/machine_learning'; -import { - resetRulesTableState, - deleteAlertsAndRules, - postDataView, - deleteDataView, -} from '../../../tasks/common'; -import { login } from '../../../tasks/login'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { createSavedQuery, deleteSavedQueries } from '../../../../tasks/api_calls/saved_queries'; +import { fetchMachineLearningModules } from '../../../../tasks/api_calls/machine_learning'; +import { resetRulesTableState } from '../../../../tasks/common'; +import { login } from '../../../../tasks/login'; import { assertRuleInstallationSuccessToastShown, assertRulesNotPresentInAddPrebuiltRulesTable, @@ -41,7 +36,7 @@ import { assertRuleUpgradeSuccessToastShown, clickAddElasticRulesButton, clickRuleUpdatesTab, -} from '../../../tasks/prebuilt_rules'; +} from '../../../../tasks/prebuilt_rules'; import { assertAlertSuppressionPropertiesShown, assertCommonPropertiesShown, @@ -60,8 +55,13 @@ import { closeRulePreview, openRuleInstallPreview, openRuleUpdatePreview, -} from '../../../tasks/prebuilt_rules_preview'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +} from '../../../../tasks/prebuilt_rules_preview'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; +import { + deleteAlertsAndRules, + deleteDataView, + postDataView, +} from '../../../../tasks/api_calls/common'; const TEST_ENV_TAGS = ['@ess', '@serverless']; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.ts similarity index 88% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.ts index 2e38b4782b433..d858280dd5294 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { getUpgradeSingleRuleButtonByRuleId, NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE, @@ -13,21 +13,22 @@ import { SELECT_ALL_RULES_ON_PAGE_CHECKBOX, UPGRADE_ALL_RULES_BUTTON, UPGRADE_SELECTED_RULES_BUTTON, -} from '../../../screens/alerts_detection_rules'; -import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; +} from '../../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../../tasks/alerts_detection_rules'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; -import { login } from '../../../tasks/login'; +} from '../../../../tasks/api_calls/prebuilt_rules'; +import { resetRulesTableState } from '../../../../tasks/common'; +import { login } from '../../../../tasks/login'; import { assertRulesNotPresentInRuleUpdatesTable, assertRuleUpgradeSuccessToastShown, assertUpgradeRequestIsComplete, clickRuleUpdatesTab, -} from '../../../tasks/prebuilt_rules'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; +} from '../../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../../tasks/rules_management'; describe( 'Detection rules, Prebuilt Rules Installation and Update workflow', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts index 1376e486790b7..51aa3f406b8ed 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/related_integrations/related_integrations.cy.ts @@ -26,14 +26,14 @@ import { disableRelatedIntegrations, enableRelatedIntegrations, } from '../../../../tasks/api_calls/kibana_advanced_settings'; -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../tasks/rules_management'; import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule'; import { installIntegrations, PackagePolicyWithoutAgentPolicyId, -} from '../../../../tasks/integrations'; +} from '../../../../tasks/api_calls/integrations'; import { disableAutoRefresh, openIntegrationsPopover, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts index fd6b28fbe57ec..dd053ab958aab 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common'; import { goToRuleDetailsOf, expectManagementTableRules, @@ -21,7 +22,7 @@ import { login } from '../../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../../tasks/rules_management'; import { createRule } from '../../../../../tasks/api_calls/rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../../../tasks/common'; +import { resetRulesTableState } from '../../../../../tasks/common'; import { getNewRule } from '../../../../../objects/rule'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts index 920c03529c112..94b6804b31193 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common'; import { MODAL_CONFIRMATION_BTN, MODAL_CONFIRMATION_BODY, @@ -79,7 +80,7 @@ import { login } from '../../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../../tasks/rules_management'; import { createRule } from '../../../../../tasks/api_calls/rules'; import { loadPrepackagedTimelineTemplates } from '../../../../../tasks/api_calls/timelines'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../../../tasks/common'; +import { resetRulesTableState } from '../../../../../tasks/common'; import { getEqlRule, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts index ac6c723dbc0c6..9a4d3cdc1b1d4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts @@ -20,7 +20,7 @@ import { } from '../../../../../screens/rules_bulk_actions'; import { actionFormSelector } from '../../../../../screens/common/rule_actions'; -import { deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common'; +import { deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/api_calls/common'; import type { RuleActionCustomFrequency } from '../../../../../tasks/common/rule_actions'; import { addSlackRuleAction, @@ -148,7 +148,7 @@ describe( context('Restricted action privileges', () => { it("User with no privileges can't add rule actions", () => { login(ROLES.hunter_no_actions); - visitRulesManagementTable(ROLES.hunter_no_actions); + visitRulesManagementTable(); expectManagementTableRules([ ruleNameToAssert, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts index 21adb447d2ce3..3b9ddf73ad3c7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts @@ -39,7 +39,11 @@ import { login } from '../../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../../tasks/rules_management'; import { createRule } from '../../../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules, deleteDataView, postDataView } from '../../../../../tasks/common'; +import { + deleteAlertsAndRules, + deleteDataView, + postDataView, +} from '../../../../../tasks/api_calls/common'; import { getEqlRule, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/deletion/rule_delete.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/deletion/rule_delete.cy.ts index 0896438e275e3..4c9168744920d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/deletion/rule_delete.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/deletion/rule_delete.cy.ts @@ -18,7 +18,7 @@ import { } from '../../../../../tasks/alerts_detection_rules'; import { deleteSelectedRules } from '../../../../../tasks/rules_bulk_actions'; import { createRule, findAllRules } from '../../../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common'; import { login } from '../../../../../tasks/login'; describe('Rule deletion', { tags: ['@ess', '@serverless'] }, () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts index 0fb3d6f08613f..0cfcc43714497 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts @@ -7,6 +7,7 @@ import path from 'path'; +import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common'; import { expectedExportedRule, getNewRule } from '../../../../../objects/rule'; import { TOASTER_BODY, @@ -29,7 +30,7 @@ import { } from '../../../../../tasks/api_calls/exceptions'; import { getExceptionList } from '../../../../../objects/exception'; import { createRule } from '../../../../../tasks/api_calls/rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../../../tasks/common'; +import { resetRulesTableState } from '../../../../../tasks/common'; import { login } from '../../../../../tasks/login'; import { visit } from '../../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/import_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/import_rules.cy.ts index 197ad5a9a82f5..4b8fe5b5312b0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/import_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/import_rules.cy.ts @@ -11,7 +11,7 @@ import { importRules, importRulesWithOverwriteAll, } from '../../../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common'; import { deleteExceptionList } from '../../../../../tasks/api_calls/exceptions'; import { login } from '../../../../../tasks/login'; import { visit } from '../../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts index 025aff9510d9d..7e753d42b6b6d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/snoozing/rule_snoozing.cy.ts @@ -9,7 +9,7 @@ import { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/comm import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { createRule, snoozeRule as snoozeRuleViaAPI } from '../../../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common'; +import { deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/api_calls/common'; import { login } from '../../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../../tasks/rules_management'; import { getNewRule } from '../../../../../objects/rule'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts similarity index 88% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/common_flows.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts index 5e5af0e6ad4a7..0610786fc1b89 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/common_flows.cy.ts @@ -5,17 +5,17 @@ * 2.0. */ -import { deleteRuleFromDetailsPage } from '../../../tasks/alerts_detection_rules'; +import { deleteRuleFromDetailsPage } from '../../../../tasks/alerts_detection_rules'; import { CUSTOM_RULES_BTN, RULES_MANAGEMENT_TABLE, RULES_ROW, -} from '../../../screens/alerts_detection_rules'; -import { createRule } from '../../../tasks/api_calls/rules'; -import { getDetails } from '../../../tasks/rule_details'; -import { ruleFields } from '../../../data/detection_engine'; -import { getTimeline } from '../../../objects/timeline'; -import { getExistingRule, getNewRule } from '../../../objects/rule'; +} from '../../../../screens/alerts_detection_rules'; +import { createRule } from '../../../../tasks/api_calls/rules'; +import { getDetails } from '../../../../tasks/rule_details'; +import { ruleFields } from '../../../../data/detection_engine'; +import { getTimeline } from '../../../../objects/timeline'; +import { getExistingRule, getNewRule } from '../../../../objects/rule'; import { ABOUT_DETAILS, @@ -42,13 +42,13 @@ import { THREAT_TACTIC, THREAT_TECHNIQUE, TIMELINE_TEMPLATE_DETAILS, -} from '../../../screens/rule_details'; +} from '../../../../screens/rule_details'; -import { createTimeline } from '../../../tasks/api_calls/timelines'; -import { deleteAlertsAndRules, deleteConnectors } from '../../../tasks/common'; -import { login } from '../../../tasks/login'; -import { visit } from '../../../tasks/navigation'; -import { ruleDetailsUrl } from '../../../urls/rule_details'; +import { createTimeline } from '../../../../tasks/api_calls/timelines'; +import { deleteAlertsAndRules, deleteConnectors } from '../../../../tasks/api_calls/common'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { ruleDetailsUrl } from '../../../../urls/rule_details'; // This test is meant to test all common aspects of the rule details page that should function // the same regardless of rule type. For any rule type specific functionalities, please include diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/esql_rule.cy.ts similarity index 69% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/esql_rule.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/esql_rule.cy.ts index 93100216692a4..c59b7db55c743 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_details/esql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/esql_rule.cy.ts @@ -5,24 +5,24 @@ * 2.0. */ -import { getEsqlRule } from '../../../objects/rule'; +import { getEsqlRule } from '../../../../objects/rule'; import { ESQL_QUERY_DETAILS, DEFINITION_DETAILS, RULE_NAME_HEADER, RULE_TYPE_DETAILS, -} from '../../../screens/rule_details'; +} from '../../../../screens/rule_details'; -import { createRule } from '../../../tasks/api_calls/rules'; +import { createRule } from '../../../../tasks/api_calls/rules'; -import { getDetails } from '../../../tasks/rule_details'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { getDetails } from '../../../../tasks/rule_details'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -import { login } from '../../../tasks/login'; -import { visit } from '../../../tasks/navigation'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; -import { ruleDetailsUrl } from '../../../urls/rule_details'; +import { ruleDetailsUrl } from '../../../../urls/rule_details'; describe('Detection ES|QL rules, details view', { tags: ['@ess'] }, () => { const rule = getEsqlRule(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts index b76b862c70d02..117fc0eee632b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { resetRulesTableState, deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { resetRulesTableState } from '../../../../tasks/common'; import { login } from '../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../tasks/rules_management'; import { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts index cf81271c1ad33..ace4406b1c22a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_links.cy.ts @@ -8,7 +8,7 @@ import { getNewRule } from '../../../../objects/rule'; import { RULES_MONITORING_TAB, RULE_NAME } from '../../../../screens/alerts_detection_rules'; import { createRule } from '../../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/create_runtime_field.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/create_runtime_field.cy.ts index 2fd13f8b6696d..6838532d55938 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/create_runtime_field.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/create_runtime_field.cy.ts @@ -19,9 +19,9 @@ import { refreshPage } from '../../../tasks/security_header'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { createField } from '../../../tasks/create_runtime_field'; import { openAlertsFieldBrowser } from '../../../tasks/alerts'; -import { deleteRuntimeField } from '../../../tasks/sourcerer'; import { GET_DATA_GRID_HEADER } from '../../../screens/common/data_grid'; import { GET_TIMELINE_HEADER } from '../../../screens/timeline'; +import { deleteRuntimeField } from '../../../tasks/api_calls/sourcerer'; const alertRunTimeField = 'field.name.alert.page'; const timelineRuntimeField = 'field.name.timeline'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts index dbf5a5975f666..d27444e3d9a82 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts @@ -26,7 +26,7 @@ import { resetSourcerer, saveSourcerer, } from '../../../tasks/sourcerer'; -import { postDataView } from '../../../tasks/common'; +import { postDataView } from '../../../tasks/api_calls/common'; import { SOURCERER } from '../../../screens/sourcerer'; const siemDataViewTitle = 'Security Default Data View'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts index 52b8ccee82156..e26756924b88e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts @@ -5,31 +5,24 @@ * 2.0. */ -import { loginWithUser } from '../../../tasks/login'; -import { visitWithUser } from '../../../tasks/navigation'; +import { ROLES } from '@kbn/security-solution-plugin/common/test'; import { hostsUrl } from '../../../urls/navigation'; -import { postDataView } from '../../../tasks/common'; -import { - createUsersAndRoles, - secReadCasesAll, - secReadCasesAllUser, -} from '../../../tasks/privileges'; +import { postDataView } from '../../../tasks/api_calls/common'; import { TOASTER } from '../../../screens/configure_cases'; +import { visit } from '../../../tasks/navigation'; +import { login } from '../../../tasks/login'; -const usersToCreate = [secReadCasesAllUser]; -const rolesToCreate = [secReadCasesAll]; const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kibana*,fakebeat-*']; -describe('Sourcerer permissions', { tags: ['@ess', '@skipInServerless'] }, () => { +describe('Sourcerer permissions', { tags: ['@ess', '@brokenInServerless'] }, () => { before(() => { dataViews.forEach((dataView: string) => postDataView(dataView)); - createUsersAndRoles(usersToCreate, rolesToCreate); }); - it(`role(s) ${secReadCasesAllUser.roles.join()} shows error when user does not have permissions`, () => { - loginWithUser(secReadCasesAllUser); - visitWithUser(hostsUrl('allHosts'), secReadCasesAllUser); + it(`role Hunter No actions shows error when user does not have permissions`, () => { + login(ROLES.hunter_no_actions); + visit(hostsUrl('allHosts')); cy.get(TOASTER).should('have.text', 'Write role required to generate data'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists/permissions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists/permissions.cy.ts index ddc1f939c08fe..94ed3c97e5911 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists/permissions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists/permissions.cy.ts @@ -16,7 +16,7 @@ describe('value list permissions', { tags: ['@ess', '@skipInServerless'] }, () = describe('user with restricted access role', () => { it('Does not allow a t1 analyst user to upload a value list', () => { login(ROLES.t1_analyst); - visit(RULES_MANAGEMENT_URL, { role: ROLES.t1_analyst }); + visit(RULES_MANAGEMENT_URL); cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts index fe8d51cae795c..60b93650048d9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts @@ -17,7 +17,7 @@ import { import { ENRICHED_DATA_ROW } from '../../screens/alerts_details'; import { createRule } from '../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../tasks/common'; +import { deleteAlertsAndRules } from '../../tasks/api_calls/common'; import { waitForAlertsToPopulate } from '../../tasks/create_new_rule'; import { expandFirstAlert, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page_privileges_callout.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page_privileges_callout.ts new file mode 100644 index 0000000000000..a00a5a9ee92cb --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/entity_analytics_management_page_privileges_callout.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ROLES } from '@kbn/security-solution-plugin/common/test'; +import { RISK_ENGINE_PRIVILEGES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + RISK_SCORE_PRIVILEGES_CALLOUT, + RISK_SCORE_STATUS_LOADING, +} from '../../screens/entity_analytics_management'; + +import { login } from '../../tasks/login'; +import { visit } from '../../tasks/navigation'; +import { ENTITY_ANALYTICS_MANAGEMENT_URL } from '../../urls/navigation'; + +const loadPageAsUserWithNoPrivileges = () => { + login(ROLES.no_risk_engine_privileges); + visit(ENTITY_ANALYTICS_MANAGEMENT_URL); +}; + +// this test suite doesn't run on serverless because it requires a custom role +describe( + 'Entity analytics management page - Risk Engine Privileges Callout', + { + tags: ['@ess'], + env: { + ftrConfig: { enableExperimental: ['riskEnginePrivilegesRouteEnabled'] }, + }, + }, + () => { + it('should not show the callout for superuser', () => { + cy.intercept(RISK_ENGINE_PRIVILEGES_URL).as('getPrivileges'); + login(); + visit(ENTITY_ANALYTICS_MANAGEMENT_URL); + cy.wait('@getPrivileges', { timeout: 15000 }); + cy.get(RISK_SCORE_STATUS_LOADING).should('not.exist'); + cy.get(RISK_SCORE_PRIVILEGES_CALLOUT).should('not.exist'); + }); + + it('should show the callout for user without risk engine privileges', () => { + cy.intercept(RISK_ENGINE_PRIVILEGES_URL).as('getPrivileges'); + loadPageAsUserWithNoPrivileges(); + cy.get(RISK_SCORE_STATUS_LOADING).should('not.exist'); + cy.wait('@getPrivileges', { timeout: 15000 }); + cy.get(RISK_SCORE_PRIVILEGES_CALLOUT); + cy.get(RISK_SCORE_PRIVILEGES_CALLOUT).should( + 'contain', + 'Missing read, write privileges for the risk-score.risk-score-* index.' + ); + cy.get(RISK_SCORE_PRIVILEGES_CALLOUT).should('contain', 'manage_index_templates'); + cy.get(RISK_SCORE_PRIVILEGES_CALLOUT).should('contain', 'manage_transform'); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts index 8914b0c98076b..c4b605b85dcb6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { expandFirstAlert, goToClosedAlertsOnRuleDetailsPage, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts index ee10a2e702b0e..8dccaa04bdc87 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts @@ -26,7 +26,7 @@ import { import { login } from '../../../../tasks/login'; import { goToExceptionsTab, visitRuleDetailsPage } from '../../../../tasks/rule_details'; -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { ADD_AND_BTN, ENTRY_DELETE_BTN, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts index 986bcb107124d..93e79ba9fa53e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts @@ -10,7 +10,7 @@ import { goToClosedAlertsOnRuleDetailsPage, waitForAlerts, } from '../../../../tasks/alerts'; -import { deleteAlertsAndRules, postDataView } from '../../../../tasks/common'; +import { deleteAlertsAndRules, postDataView } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visitRuleDetailsPage } from '../../../../tasks/rule_details'; import { createRule } from '../../../../tasks/api_calls/rules'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts index 3c613f32d2c73..72c18b27a9b2a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts @@ -47,7 +47,7 @@ import { FIELD_INPUT_PARENT, } from '../../../screens/exceptions'; -import { deleteAlertsAndRules, reload } from '../../../tasks/common'; +import { reload } from '../../../tasks/common'; import { createExceptionList, createExceptionListItem, @@ -55,6 +55,7 @@ import { deleteExceptionList, } from '../../../tasks/api_calls/exceptions'; import { getExceptionList } from '../../../objects/exception'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; // TODO: https://github.com/elastic/kibana/issues/161539 // Test Skipped until we fix the Flyout rerendering issue diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/match_any.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/match_any.cy.ts index f18b056c4e254..282e8d3c81223 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/match_any.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/match_any.cy.ts @@ -26,7 +26,7 @@ import { submitNewExceptionItem, } from '../../../tasks/exceptions'; import { CONFIRM_BTN } from '../../../screens/exceptions'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { ALERTS_COUNT } from '../../../screens/alerts'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts index 136038f641ec9..511343abc8a76 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts @@ -25,7 +25,7 @@ import { EXCEPTION_ITEM_VIEWER_CONTAINER, } from '../../../screens/exceptions'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; describe( 'Add multiple conditions and validate the generated exceptions', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts index 8ec40a0e36436..e75c0eb8d81b0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts @@ -30,7 +30,7 @@ import { deleteAlertsAndRules, deleteEndpointExceptionList, deleteExceptionLists, -} from '../../../tasks/common'; +} from '../../../tasks/api_calls/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts index 6cc022873aea5..a06b76455dfbc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts @@ -37,7 +37,7 @@ import { submitEditedExceptionItem, submitNewExceptionItem, } from '../../../tasks/exceptions'; -import { deleteAlertsAndRules, deleteExceptionLists } from '../../../tasks/common'; +import { deleteAlertsAndRules, deleteExceptionLists } from '../../../tasks/api_calls/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts index 79f6638e6c0f7..a4f0daa190e49 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts @@ -28,7 +28,7 @@ import { waitForTheRuleToBeExecuted, } from '../../../tasks/rule_details'; -import { postDataView, deleteAlertsAndRules } from '../../../tasks/common'; +import { postDataView, deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts index 935668db1a5a6..5cb1ac70818ca 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/read_only_view.cy.ts @@ -13,7 +13,7 @@ import { login } from '../../../tasks/login'; import { visitRulesManagementTable } from '../../../tasks/rules_management'; import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details'; import { goToRuleDetailsOf } from '../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, @@ -55,7 +55,7 @@ describe('Exceptions viewer read only', { tags: ['@ess'] }, () => { }); login(ROLES.t1_analyst); - visitRulesManagementTable(ROLES.t1_analyst); + visitRulesManagementTable(); goToRuleDetailsOf('Test exceptions rule'); goToExceptionsTab(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts index 9f64fac5f1512..dd21e8571673c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts @@ -40,30 +40,28 @@ import { findSharedExceptionListItemsByName, } from '../../../tasks/exceptions_table'; import { visitRuleDetailsPage } from '../../../tasks/rule_details'; -import { deleteEndpointExceptionList, deleteExceptionLists } from '../../../tasks/common'; +import { + deleteAlertsAndRules, + deleteEndpointExceptionList, + deleteExceptionLists, +} from '../../../tasks/api_calls/common'; -// https://github.com/elastic/kibana/issues/171235 -// FLAKY: https://github.com/elastic/kibana/issues/171242 -describe.skip('Add, edit and delete exception', { tags: ['@ess', '@serverless'] }, () => { +describe('Manage exceptions', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); + deleteAlertsAndRules(); deleteExceptionLists(); deleteEndpointExceptionList(); - cy.task('esArchiverLoad', { archiveName: 'exceptions' }); createRule(getNewRule()).as('createdRule'); visit(EXCEPTIONS_URL); }); - afterEach(() => { - cy.task('esArchiverUnload', 'exceptions'); - }); - const exceptionName = 'My item name'; const exceptionNameEdited = 'My item name edited'; const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name'; const EXCEPTION_LIST_NAME = 'Newly created list'; - describe('Add, Edit and delete Exception item', () => { + describe('Add, edit and delete exception item', () => { it('should create exception item from Shared Exception List page and linked to a Rule', () => { // Click on "Create shared exception list" button on the header // Click on "Create exception item" diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts index af8edaa017b81..a4c251617b5f8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { deleteAlertsAndRules, deleteExceptionLists } from '../../../../tasks/common'; +import { deleteAlertsAndRules, deleteExceptionLists } from '../../../../tasks/api_calls/common'; import { createRule } from '../../../../tasks/api_calls/rules'; import { getExceptionList } from '../../../../objects/exception'; import { assertNumberOfExceptionItemsExists } from '../../../../tasks/exceptions'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts index b115508e2b598..8a7ee172aed30 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts @@ -32,7 +32,7 @@ describe('Shared exception lists - read only', { tags: ['@ess', '@skipInServerle createExceptionList(getExceptionList(), getExceptionList().list_id); login(ROLES.t1_analyst); - visit(EXCEPTIONS_URL, { role: ROLES.t1_analyst }); + visit(EXCEPTIONS_URL); // Using cy.contains because we do not care about the exact text, // just checking number of lists shown diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_alert_to_case.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_alert_to_case.cy.ts index a105040361a47..6b40e1d336186 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_alert_to_case.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_alert_to_case.cy.ts @@ -20,7 +20,7 @@ import { LOADING_INDICATOR } from '../../../screens/security_header'; const loadDetectionsPage = (role: SecurityRoleName) => { login(role); - visit(ALERTS_URL, { role }); + visit(ALERTS_URL); waitForAlertsToPopulate(); }; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts index e040c98730986..c0a2295887a1f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/attach_timeline.cy.ts @@ -17,7 +17,7 @@ import { DESCRIPTION_INPUT, ADD_COMMENT_INPUT } from '../../../screens/create_ne import { getCase1 } from '../../../objects/case'; import { getTimeline } from '../../../objects/timeline'; import { createTimeline } from '../../../tasks/api_calls/timelines'; -import { deleteTimelines } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; import { createCase } from '../../../tasks/api_calls/cases'; describe('attach timeline to case', { tags: ['@ess', '@serverless'] }, () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/connectors.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/connectors.cy.ts index aaa785cc8f392..31c2068b49db0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/connectors.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/connectors.cy.ts @@ -10,7 +10,7 @@ import { getServiceNowConnector, getServiceNowITSMHealthResponse } from '../../. import { SERVICE_NOW_MAPPING } from '../../../screens/configure_cases'; import { goToEditExternalConnection } from '../../../tasks/all_cases'; -import { deleteAllCasesItems, deleteConnectors } from '../../../tasks/common'; +import { deleteAllCasesItems, deleteConnectors } from '../../../tasks/api_calls/common'; import { addServiceNowConnector, openAddNewConnectorOption, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts index ef24d84ee7624..45996eb53b4b5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/creation.cy.ts @@ -34,7 +34,7 @@ import { CASES_METRIC, UNEXPECTED_METRICS, } from '../../../screens/case_details'; -import { TIMELINE_DESCRIPTION, TIMELINE_QUERY, TIMELINE_TITLE } from '../../../screens/timeline'; +import { TIMELINE_QUERY, TIMELINE_TITLE } from '../../../screens/timeline'; import { OVERVIEW_CASE_DESCRIPTION, OVERVIEW_CASE_NAME } from '../../../screens/overview'; @@ -123,7 +123,6 @@ describe('Cases', { tags: ['@ess', '@serverless'] }, () => { openCaseTimeline(); cy.get(TIMELINE_TITLE).contains(this.mycase.timeline.title); - cy.get(TIMELINE_DESCRIPTION).contains(this.mycase.timeline.description); cy.get(TIMELINE_QUERY).should('have.text', this.mycase.timeline.query); visitWithTimeRange(OVERVIEW_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/privileges.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/privileges.cy.ts index f1cdbc0d7af90..7768a892d5d09 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/privileges.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/cases/privileges.cy.ts @@ -9,7 +9,7 @@ import type { TestCaseWithoutTimeline } from '../../../objects/case'; import { ALL_CASES_CREATE_NEW_CASE_BTN, ALL_CASES_NAME } from '../../../screens/all_cases'; import { goToCreateNewCase } from '../../../tasks/all_cases'; -import { deleteAllCasesItems } from '../../../tasks/common'; +import { deleteAllCasesItems } from '../../../tasks/api_calls/common'; import { backToCases, @@ -18,7 +18,7 @@ import { filterStatusOpen, } from '../../../tasks/create_new_case'; import { login, loginWithUser } from '../../../tasks/login'; -import { visitWithUser } from '../../../tasks/navigation'; +import { visit } from '../../../tasks/navigation'; import { createUsersAndRoles, deleteUsersAndRoles, @@ -66,7 +66,7 @@ describe('Cases privileges', { tags: ['@ess'] }, () => { for (const user of [secAllUser, secReadCasesAllUser, secAllCasesNoDeleteUser]) { it(`User ${user.username} with role(s) ${user.roles.join()} can create a case`, () => { loginWithUser(user); - visitWithUser(CASES_URL, user); + visit(CASES_URL); goToCreateNewCase(); fillCasesMandatoryfields(testCase); createCase(); @@ -80,7 +80,7 @@ describe('Cases privileges', { tags: ['@ess'] }, () => { for (const user of [secAllCasesOnlyReadDeleteUser]) { it(`User ${user.username} with role(s) ${user.roles.join()} cannot create a case`, () => { loginWithUser(user); - visitWithUser(CASES_URL, user); + visit(CASES_URL); cy.get(ALL_CASES_CREATE_NEW_CASE_BTN).should('not.exist'); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts index 7210bacd1aa77..e849e0408c073 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/dashboards/entity_analytics.cy.ts @@ -11,7 +11,7 @@ import { visitWithTimeRange } from '../../../tasks/navigation'; import { ALERTS_URL, ENTITY_ANALYTICS_URL } from '../../../urls/navigation'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { ANOMALIES_TABLE, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/filters/pinned_filters.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/filters/pinned_filters.cy.ts index a9615f27984ea..516b776a86e3d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/filters/pinned_filters.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/filters/pinned_filters.cy.ts @@ -21,7 +21,7 @@ import { openKibanaNavigation, } from '../../../tasks/kibana_navigation'; import { ALERTS_PAGE } from '../../../screens/kibana_navigation'; -import { postDataView } from '../../../tasks/common'; +import { postDataView } from '../../../tasks/api_calls/common'; import { navigateToAlertsPageInServerless } from '../../../tasks/serverless/navigation'; describe('ESS - pinned filters', { tags: ['@ess'] }, () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/inspect/inspect_button.cy.ts similarity index 85% rename from x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/inspect/inspect_button.cy.ts index 30a3704879f36..86309e80fd7e9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/inspect/inspect_button.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/inspect/inspect_button.cy.ts @@ -9,17 +9,18 @@ import { INSPECT_BUTTONS_IN_SECURITY, INSPECT_MODAL, INSPECT_MODAL_INDEX_PATTERN, -} from '../../screens/inspect'; +} from '../../../screens/inspect'; import { closesModal, openLensVisualizationsInspectModal, openTab, openTableInspectModal, -} from '../../tasks/inspect'; -import { login } from '../../tasks/login'; -import { visitWithTimeRange } from '../../tasks/navigation'; -import { postDataView, waitForWelcomePanelToBeLoaded } from '../../tasks/common'; -import { selectDataView } from '../../tasks/sourcerer'; +} from '../../../tasks/inspect'; +import { login } from '../../../tasks/login'; +import { visitWithTimeRange } from '../../../tasks/navigation'; +import { waitForWelcomePanelToBeLoaded } from '../../../tasks/common'; +import { selectDataView } from '../../../tasks/sourcerer'; +import { postDataView } from '../../../tasks/api_calls/common'; const DATA_VIEW = 'auditbeat-*'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/ml/ml_conditional_links.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/ml/ml_conditional_links.cy.ts similarity index 98% rename from x-pack/test/security_solution_cypress/cypress/e2e/ml/ml_conditional_links.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/ml/ml_conditional_links.cy.ts index 061f17032c54a..c90f8bea96fd7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/ml/ml_conditional_links.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/ml/ml_conditional_links.cy.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { KQL_INPUT } from '../../screens/security_header'; +import { KQL_INPUT } from '../../../screens/security_header'; -import { login } from '../../tasks/login'; -import { visit } from '../../tasks/navigation'; +import { login } from '../../../tasks/login'; +import { visit } from '../../../tasks/navigation'; import { mlHostMultiHostKqlQuery, @@ -24,7 +24,7 @@ import { mlNetworkNullKqlQuery, mlNetworkSingleIpKqlQuery, mlNetworkSingleIpNullKqlQuery, -} from '../../urls/ml_conditional_links'; +} from '../../../urls/ml_conditional_links'; describe('ml conditional links', { tags: ['@ess', '@brokenInServerless'] }, () => { beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/navigation/navigation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/navigation.cy.ts similarity index 96% rename from x-pack/test/security_solution_cypress/cypress/e2e/navigation/navigation.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/navigation.cy.ts index 788ff1d9eac0f..906dcd0548b32 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/navigation/navigation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/navigation.cy.ts @@ -30,12 +30,12 @@ import { EXPLORE, SETTINGS, ENTITY_ANALYTICS, -} from '../../screens/security_header'; -import * as ServerlessHeaders from '../../screens/serverless_security_header'; +} from '../../../screens/security_header'; +import * as ServerlessHeaders from '../../../screens/serverless_security_header'; -import { login } from '../../tasks/login'; -import { visit, visitGetStartedPage, visitWithTimeRange } from '../../tasks/navigation'; -import { navigateFromHeaderTo } from '../../tasks/security_header'; +import { login } from '../../../tasks/login'; +import { visit, visitGetStartedPage, visitWithTimeRange } from '../../../tasks/navigation'; +import { navigateFromHeaderTo } from '../../../tasks/security_header'; import { ALERTS_URL, @@ -71,12 +71,12 @@ import { ASSETS_URL, FLEET_URL, CLOUD_DEFEND_URL, -} from '../../urls/navigation'; -import { RULES_MANAGEMENT_URL } from '../../urls/rules_management'; +} from '../../../urls/navigation'; +import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; import { openKibanaNavigation, navigateFromKibanaCollapsibleTo, -} from '../../tasks/kibana_navigation'; +} from '../../../tasks/kibana_navigation'; import { CASES_PAGE, ALERTS_PAGE, @@ -86,7 +86,7 @@ import { TIMELINES_PAGE, FINDINGS_PAGE, THREAT_INTELLIGENCE_PAGE, -} from '../../screens/kibana_navigation'; +} from '../../../screens/kibana_navigation'; describe('top-level navigation common to all pages in the Security app', { tags: '@ess' }, () => { beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/navigation/search_bar.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/search_bar.cy.ts similarity index 76% rename from x-pack/test/security_solution_cypress/cypress/e2e/navigation/search_bar.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/search_bar.cy.ts index a92e5aa1d93d5..14afab3600f54 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/navigation/search_bar.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/navigation/search_bar.cy.ts @@ -5,23 +5,23 @@ * 2.0. */ -import { login } from '../../tasks/login'; -import { visitWithTimeRange } from '../../tasks/navigation'; +import { login } from '../../../tasks/login'; +import { visitWithTimeRange } from '../../../tasks/navigation'; import { openAddFilterPopover, fillAddFilterForm, openKqlQueryBar, fillKqlQueryBar, -} from '../../tasks/search_bar'; +} from '../../../tasks/search_bar'; import { AUTO_SUGGEST_AGENT_NAME, AUTO_SUGGEST_HOST_NAME_VALUE, GLOBAL_SEARCH_BAR_FILTER_ITEM, -} from '../../screens/search_bar'; -import { getHostIpFilter } from '../../objects/filter'; +} from '../../../screens/search_bar'; +import { getHostIpFilter } from '../../../objects/filter'; -import { hostsUrl } from '../../urls/navigation'; -import { waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts'; +import { hostsUrl } from '../../../urls/navigation'; +import { waitForAllHostsToBeLoaded } from '../../../tasks/hosts/all_hosts'; describe('SearchBar', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts similarity index 85% rename from x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts index 856f112272754..fa11642abc172 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/urls/compatibility.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/compatibility.cy.ts @@ -5,18 +5,18 @@ * 2.0. */ -import { login } from '../../tasks/login'; -import { visit } from '../../tasks/navigation'; +import { login } from '../../../tasks/login'; +import { visit } from '../../../tasks/navigation'; -import { ALERTS_URL, CREATE_RULE_URL } from '../../urls/navigation'; -import { RULES_MANAGEMENT_URL } from '../../urls/rules_management'; -import { ABSOLUTE_DATE_RANGE } from '../../urls/state'; +import { ALERTS_URL, CREATE_RULE_URL } from '../../../urls/navigation'; +import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; +import { ABSOLUTE_DATE_RANGE } from '../../../urls/state'; import { DATE_PICKER_START_DATE_POPOVER_BUTTON, GET_DATE_PICKER_END_DATE_POPOVER_BUTTON, -} from '../../screens/date_picker'; -import { ruleDetailsUrl } from '../../urls/rule_details'; -import { editRuleUrl } from '../../urls/edit_rule'; +} from '../../../screens/date_picker'; +import { ruleDetailsUrl } from '../../../urls/rule_details'; +import { editRuleUrl } from '../../../urls/edit_rule'; const LEGACY_DETECTIONS_URL_1 = '/app/siem#/detections'; const LEGACY_DETECTIONS_URL_2 = '/app/security/detections'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/urls/not_found.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/not_found.cy.ts similarity index 85% rename from x-pack/test/security_solution_cypress/cypress/e2e/urls/not_found.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/not_found.cy.ts index 329e0b33144bf..9c27440a97864 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/urls/not_found.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/not_found.cy.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { login } from '../../tasks/login'; -import { visitWithTimeRange } from '../../tasks/navigation'; +import { login } from '../../../tasks/login'; +import { visitWithTimeRange } from '../../../tasks/navigation'; import { ALERTS_URL, @@ -16,12 +16,12 @@ import { TIMELINES_URL, EXCEPTIONS_URL, CREATE_RULE_URL, -} from '../../urls/navigation'; -import { RULES_MANAGEMENT_URL } from '../../urls/rules_management'; +} from '../../../urls/navigation'; +import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; -import { NOT_FOUND } from '../../screens/common/page'; -import { ruleDetailsUrl } from '../../urls/rule_details'; -import { editRuleUrl } from '../../urls/edit_rule'; +import { NOT_FOUND } from '../../../screens/common/page'; +import { ruleDetailsUrl } from '../../../urls/rule_details'; +import { editRuleUrl } from '../../../urls/edit_rule'; const mockRuleId = '5a4a0460-d822-11eb-8962-bfd4aff0a9b3'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts similarity index 92% rename from x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts index 0f2d2ad9cac3d..e24998fd9f304 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/urls/state.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/urls/state.cy.ts @@ -11,9 +11,9 @@ import { GET_LOCAL_DATE_PICKER_END_DATE_POPOVER_BUTTON, DATE_PICKER_START_DATE_POPOVER_BUTTON, GET_LOCAL_DATE_PICKER_START_DATE_POPOVER_BUTTON, -} from '../../screens/date_picker'; -import { HOSTS_NAMES } from '../../screens/hosts/all_hosts'; -import { ANOMALIES_TAB } from '../../screens/hosts/main'; +} from '../../../screens/date_picker'; +import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts'; +import { ANOMALIES_TAB } from '../../../screens/hosts/main'; import { BREADCRUMBS, EXPLORE_PANEL_BTN, @@ -22,38 +22,38 @@ import { NETWORK, LOADING_INDICATOR, openNavigationPanel, -} from '../../screens/security_header'; -import { TIMELINE_DATE_PICKER_CONTAINER, TIMELINE_TITLE } from '../../screens/timeline'; +} from '../../../screens/security_header'; +import { TIMELINE_DATE_PICKER_CONTAINER, TIMELINE_TITLE } from '../../../screens/timeline'; -import { login } from '../../tasks/login'; -import { visit, visitWithTimeRange } from '../../tasks/navigation'; +import { login } from '../../../tasks/login'; +import { visit, visitWithTimeRange } from '../../../tasks/navigation'; import { updateDates, setStartDate, setEndDate, updateTimelineDates, -} from '../../tasks/date_picker'; -import { openFirstHostDetails, waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts'; -import { openAllHosts } from '../../tasks/hosts/main'; +} from '../../../tasks/date_picker'; +import { openFirstHostDetails, waitForAllHostsToBeLoaded } from '../../../tasks/hosts/all_hosts'; +import { openAllHosts } from '../../../tasks/hosts/main'; -import { waitForIpsTableToBeLoaded } from '../../tasks/network/flows'; +import { waitForIpsTableToBeLoaded } from '../../../tasks/network/flows'; import { clearSearchBar, kqlSearch, navigateFromHeaderTo, saveQuery, -} from '../../tasks/security_header'; -import { openTimelineUsingToggle } from '../../tasks/security_main'; -import { addNameToTimelineAndSave, closeTimeline, populateTimeline } from '../../tasks/timeline'; +} from '../../../tasks/security_header'; +import { openTimelineUsingToggle } from '../../../tasks/security_main'; +import { addNameToTimelineAndSave, closeTimeline, populateTimeline } from '../../../tasks/timeline'; -import { hostsUrl } from '../../urls/navigation'; -import { ABSOLUTE_DATE_RANGE } from '../../urls/state'; +import { hostsUrl } from '../../../urls/navigation'; +import { ABSOLUTE_DATE_RANGE } from '../../../urls/state'; -import { getTimeline } from '../../objects/timeline'; +import { getTimeline } from '../../../objects/timeline'; import { GLOBAL_SEARCH_BAR_FILTER_ITEM_AT, GLOBAL_SEARCH_BAR_PINNED_FILTER, -} from '../../screens/search_bar'; +} from '../../../screens/search_bar'; const ABSOLUTE_DATE = { endTime: 'Aug 1, 2019 @ 20:33:29.186', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts index 9ab0d07569421..7e5e19b464c4a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts @@ -25,7 +25,7 @@ import { openTable, } from '../../../tasks/alerts_details'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { login } from '../../../tasks/login'; import { visit, visitWithTimeRange } from '../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_table_api_calls.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_table_api_calls.cy.ts new file mode 100644 index 0000000000000..3ef2c0dd21b64 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_table_api_calls.cy.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getNewRule } from '../../../objects/rule'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { login } from '../../../tasks/login'; +import { visit } from '../../../tasks/navigation'; +import { ALERTS_URL } from '../../../urls/navigation'; + +/* + * + * Alert table is third party component which cannot be easily tested by jest. + * This test main checks if Alert Table does not call api/lists/index more than once. + * + * */ + +describe('Alert Table API calls', { tags: ['@ess', '@serverless'] }, () => { + let callCount: number = 0; + + beforeEach(() => { + callCount = 0; + login(); + createRule(getNewRule()); + // intercept all calls to `api/lists/index` + // and count how many times it was called + cy.intercept('GET', '/api/lists/index', (req) => { + req.on('response', (res) => { + if (res.statusCode === 200) { + callCount += 1; + } + }); + }); + + visit(ALERTS_URL); + waitForAlertsToPopulate(); + }); + + it('should call `api/lists/index` only once', () => { + cy.get('[data-test-subj="alertsTable"]').then(() => { + expect(callCount, 'number of times lists index api is called').to.equal(1); + }); + }); +}); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts index f06fad0cd43ee..2dc4a360134b2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts @@ -32,7 +32,7 @@ import { parseAlertsCountToInt, } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts index dd29284f6c0ce..1097a8357167b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts @@ -51,7 +51,7 @@ import { import { TOASTER } from '../../../screens/alerts_detection_rules'; import { setEndDate, setStartDate } from '../../../tasks/date_picker'; import { fillAddFilterForm, openAddFilterPopover } from '../../../tasks/search_bar'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; const customFilters = [ { @@ -107,7 +107,8 @@ const assertFilterControlsWithFilterObject = ( }); }; -describe(`Detections : Page Filters`, { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/171890 +describe.skip(`Detections : Page Filters`, { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { deleteAlertsAndRules(); createRule(getNewRule({ rule_id: 'custom_rule_filters' })); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts index 6e0b437f2e2e6..aaa8806362d06 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts @@ -51,7 +51,7 @@ import { openTakeActionButtonAndSelectItem, selectTakeActionItem, } from '../../../../tasks/expandable_flyout/alert_details_right_panel'; -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; import { createRule } from '../../../../tasks/api_calls/rules'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index 33bcb93caacf0..fb2af6a16b022 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { collapseDocumentDetailsExpandableFlyoutLeftSection } from '../../../../tasks/expandable_flyout/alert_details_right_panel'; import { createNewCaseFromExpandableFlyout, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_table_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_table_tab.cy.ts index aa320000a256c..c855beca9fdb6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_table_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_table_tab.cy.ts @@ -25,7 +25,7 @@ import { filterTableTabTable, toggleColumnTableTabTable, } from '../../../../tasks/expandable_flyout/alert_details_right_panel_table_tab'; -import { deleteAlertsAndRules } from '../../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; import { createRule } from '../../../../tasks/api_calls/rules'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_url_sync.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_url_sync.cy.ts index fae802cbfc190..0cd12d9381ad9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_url_sync.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_url_sync.cy.ts @@ -26,13 +26,13 @@ describe('Expandable flyout state sync', { tags: ['@ess', '@serverless'] }, () = }); it('should test flyout url sync', () => { - cy.url().should('not.include', 'eventFlyout'); + cy.url().should('not.include', 'rightPanel'); expandFirstAlertExpandableFlyout(); cy.log('should serialize its state to url'); - cy.url().should('include', 'eventFlyout'); + cy.url().should('include', 'rightPanel'); cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('have.text', rule.name); cy.log('should reopen the flyout after browser refresh'); @@ -40,13 +40,13 @@ describe('Expandable flyout state sync', { tags: ['@ess', '@serverless'] }, () = cy.reload(); waitForAlertsToPopulate(); - cy.url().should('include', 'eventFlyout'); + cy.url().should('include', 'rightPanel'); cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('have.text', rule.name); cy.log('should clear the url state when flyout is closed'); closeFlyout(); - cy.url().should('not.include', 'eventFlyout'); + cy.url().should('not.include', 'rightPanel'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/investigate_in_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/investigate_in_timeline.cy.ts index 0570557d33f21..8095921c6df86 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/investigate_in_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/investigate_in_timeline.cy.ts @@ -7,8 +7,12 @@ import { disableExpandableFlyout } from '../../../tasks/api_calls/kibana_advanced_settings'; import { getNewRule } from '../../../objects/rule'; -import { PROVIDER_BADGE, QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../../screens/timeline'; -import { FILTER_BADGE } from '../../../screens/alerts'; +import { + PROVIDER_BADGE, + QUERY_TAB_BUTTON, + TIMELINE_FILTER_BADGE, + TIMELINE_TITLE, +} from '../../../screens/timeline'; import { expandFirstAlert, investigateFirstAlertInTimeline } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; @@ -80,7 +84,7 @@ describe('Investigate in timeline', { tags: ['@ess', '@serverless'] }, () => { cy.get(QUERY_TAB_BUTTON).should('contain.text', alertCount); // The correct filter is applied to the timeline query - cy.get(FILTER_BADGE).should( + cy.get(TIMELINE_FILTER_BADGE).should( 'have.text', ' {"bool":{"must":[{"term":{"process.args":"-zsh"}},{"term":{"process.args":"unique"}}]}}' ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts index 98d76d984512d..b90413cdfe751 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/ransomware_prevention.cy.ts @@ -12,7 +12,7 @@ import { ALERTS_URL } from '../../../urls/navigation'; import { ALERTS_HISTOGRAM_SERIES, ALERT_RULE_NAME, MESSAGE } from '../../../screens/alerts'; import { TIMELINE_VIEW_IN_ANALYZER } from '../../../screens/timeline'; import { selectAlertsHistogram } from '../../../tasks/alerts'; -import { deleteTimelines } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; import { createTimeline } from '../../../tasks/api_calls/timelines'; import { getTimeline } from '../../../objects/timeline'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts index e19e4ca043b3c..eb3881f8123a5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts @@ -31,7 +31,7 @@ import { import { QUERY_TAB_BUTTON, TIMELINE_DATA_PROVIDERS_CONTAINER } from '../../../screens/timeline'; import { waitForAlerts } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; import { investigateDashboardItemInTimeline } from '../../../tasks/dashboards/common'; import { waitToNavigateAwayFrom } from '../../../tasks/kibana_navigation'; import { login } from '../../../tasks/login'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts index b3ed43ffe9d8a..9054684c53e82 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts @@ -26,7 +26,7 @@ import { TIMELINES_FAVORITE, } from '../../../screens/timelines'; import { createTimeline } from '../../../tasks/api_calls/timelines'; -import { deleteTimelines } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; @@ -111,7 +111,6 @@ describe('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { addNameToTimelineAndSave('Test'); cy.wait('@timeline', { timeout: 100000 }); cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible'); - cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description); cy.get(TIMELINE_QUERY).should('have.text', getTimeline().query); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts index a50dbe08b0637..96b30a29d23d2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/correlation_tab.cy.ts @@ -21,7 +21,7 @@ import { addEqlToTimeline, saveTimeline } from '../../../tasks/timeline'; import { TIMELINES_URL } from '../../../urls/navigation'; import { EQL_QUERY_VALIDATION_ERROR } from '../../../screens/create_new_rule'; -import { deleteTimelines } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; describe('Correlation tab', { tags: ['@ess', '@serverless'] }, () => { const eql = 'any where process.name == "zsh"'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts index 546cdaa6b64c1..c9163f7da515b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts @@ -12,19 +12,19 @@ import { LOCKED_ICON, NOTES_TEXT, PIN_EVENT, - TIMELINE_DESCRIPTION, TIMELINE_FILTER, TIMELINE_FLYOUT_WRAPPER, TIMELINE_QUERY, TIMELINE_PANEL, TIMELINE_STATUS, TIMELINE_TAB_CONTENT_GRAPHS_NOTES, - TIMELINE_SAVE_MODAL_OPEN_BUTTON, - SAVE_TIMELINE_BTN_TOOLTIP, + SAVE_TIMELINE_ACTION_BTN, + SAVE_TIMELINE_TOOLTIP, } from '../../../screens/timeline'; +import { ROWS } from '../../../screens/timelines'; import { createTimelineTemplate } from '../../../tasks/api_calls/timelines'; -import { deleteTimelines } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; import { login } from '../../../tasks/login'; import { visit, visitWithTimeRange } from '../../../tasks/navigation'; import { openTimelineUsingToggle } from '../../../tasks/security_main'; @@ -42,9 +42,11 @@ import { pinFirstEvent, populateTimeline, addNameToTimelineAndSave, + addNameToTimelineAndSaveAsNew, } from '../../../tasks/timeline'; +import { createTimeline } from '../../../tasks/timelines'; -import { OVERVIEW_URL, TIMELINE_TEMPLATES_URL } from '../../../urls/navigation'; +import { OVERVIEW_URL, TIMELINE_TEMPLATES_URL, TIMELINES_URL } from '../../../urls/navigation'; describe('Create a timeline from a template', { tags: ['@ess', '@serverless'] }, () => { before(() => { @@ -62,9 +64,7 @@ describe('Create a timeline from a template', { tags: ['@ess', '@serverless'] }, selectCustomTemplates(); expandEventAction(); clickingOnCreateTimelineFormTemplateBtn(); - cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible'); - cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description); cy.get(TIMELINE_QUERY).should('have.text', getTimeline().query); closeTimeline(); }); @@ -75,8 +75,7 @@ describe('Timelines', (): void => { deleteTimelines(); }); - // FLAKY: https://github.com/elastic/kibana/issues/169866 - describe.skip('Toggle create timeline from plus icon', () => { + describe('Toggle create timeline from "New" btn', () => { context('Privileges: CRUD', { tags: '@ess' }, () => { beforeEach(() => { login(); @@ -84,6 +83,7 @@ describe('Timelines', (): void => { }); it('toggle create timeline ', () => { + openTimelineUsingToggle(); createNewTimeline(); addNameAndDescriptionToTimeline(getTimeline()); cy.get(TIMELINE_PANEL).should('be.visible'); @@ -93,16 +93,17 @@ describe('Timelines', (): void => { context('Privileges: READ', { tags: '@ess' }, () => { beforeEach(() => { login(ROLES.t1_analyst); - visitWithTimeRange(OVERVIEW_URL, { role: ROLES.t1_analyst }); + visitWithTimeRange(OVERVIEW_URL); }); it('should not be able to create/update timeline ', () => { + openTimelineUsingToggle(); createNewTimeline(); cy.get(TIMELINE_PANEL).should('be.visible'); - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).should('be.disabled'); - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).first().realHover(); - cy.get(SAVE_TIMELINE_BTN_TOOLTIP).should('be.visible'); - cy.get(SAVE_TIMELINE_BTN_TOOLTIP).should( + cy.get(SAVE_TIMELINE_ACTION_BTN).should('be.disabled'); + cy.get(SAVE_TIMELINE_ACTION_BTN).first().realHover(); + cy.get(SAVE_TIMELINE_TOOLTIP).should('be.visible'); + cy.get(SAVE_TIMELINE_TOOLTIP).should( 'have.text', 'You can use Timeline to investigate events, but you do not have the required permissions to save timelines for future use. If you need to save timelines, contact your Kibana administrator.' ); @@ -149,10 +150,12 @@ describe('Timelines', (): void => { } ); - describe('shows the different timeline states', () => { + // FLAKY: https://github.com/elastic/kibana/issues/172031 + describe.skip('shows the different timeline states', () => { before(() => { login(); visitWithTimeRange(OVERVIEW_URL); + openTimelineUsingToggle(); createNewTimeline(); }); @@ -179,4 +182,28 @@ describe('Timelines', (): void => { .should('match', /^Has unsaved changes/); }); }); + + describe('saves timeline as new', () => { + before(() => { + deleteTimelines(); + login(); + visitWithTimeRange(TIMELINES_URL); + }); + + it('should save timelines as new', { tags: ['@ess', '@serverless'] }, () => { + cy.get(ROWS).should('have.length', '0'); + + createTimeline(); + addNameToTimelineAndSave('First'); + addNameToTimelineAndSaveAsNew('Second'); + closeTimeline(); + + cy.get(ROWS).should('have.length', '2'); + cy.get(ROWS) + .first() + .invoke('text') + .should('match', /Second/); + cy.get(ROWS).last().invoke('text').should('match', /First/); + }); + }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/flyout_button.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/flyout_button.cy.ts index 3ba7e607bbfb1..1dd9c5cdcf1f0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/flyout_button.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/flyout_button.cy.ts @@ -6,20 +6,15 @@ */ import { TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON } from '../../../screens/security_main'; -import { CREATE_NEW_TIMELINE, TIMELINE_FLYOUT_HEADER } from '../../../screens/timeline'; +import { TIMELINE_FLYOUT_HEADER } from '../../../screens/timeline'; import { waitForAllHostsToBeLoaded } from '../../../tasks/hosts/all_hosts'; import { login } from '../../../tasks/login'; import { visitWithTimeRange } from '../../../tasks/navigation'; import { closeTimelineUsingCloseButton, - closeTimelineUsingToggle, openTimelineUsingToggle, } from '../../../tasks/security_main'; -import { - closeCreateTimelineOptionsPopover, - openCreateTimelineOptionsPopover, -} from '../../../tasks/timeline'; import { hostsUrl } from '../../../urls/navigation'; @@ -33,7 +28,7 @@ describe('timeline flyout button', () => { it('toggles open the timeline', { tags: ['@ess', '@serverless'] }, () => { openTimelineUsingToggle(); cy.get(TIMELINE_FLYOUT_HEADER).should('have.css', 'visibility', 'visible'); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); }); it( @@ -41,7 +36,7 @@ describe('timeline flyout button', () => { { tags: ['@ess', '@serverless'] }, () => { openTimelineUsingToggle(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); cy.get(TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON).should('have.focus'); } @@ -69,18 +64,6 @@ describe('timeline flyout button', () => { } ); - it( - 'the `(+)` button popover menu owns focus when open', - { tags: ['@ess', '@serverless'] }, - () => { - openCreateTimelineOptionsPopover(); - cy.get(CREATE_NEW_TIMELINE).focus(); - cy.get(CREATE_NEW_TIMELINE).should('have.focus'); - closeCreateTimelineOptionsPopover(); - cy.get(CREATE_NEW_TIMELINE).should('not.exist'); - } - ); - it( 'should render the global search dropdown when the input is focused', { tags: ['@ess'] }, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts index 372a0a3178975..e1dc678631124 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/open_timeline.cy.ts @@ -7,11 +7,7 @@ import { getTimeline } from '../../../objects/timeline'; -import { - TIMELINE_DESCRIPTION, - TIMELINE_TITLE, - OPEN_TIMELINE_MODAL, -} from '../../../screens/timeline'; +import { TIMELINE_TITLE, OPEN_TIMELINE_MODAL } from '../../../screens/timeline'; import { TIMELINES_DESCRIPTION, TIMELINES_PINNED_EVENT_COUNT, @@ -69,7 +65,6 @@ describe('Open timeline', { tags: ['@serverless', '@ess'] }, () => { cy.get(TIMELINES_NOTES_COUNT).last().should('have.text', '1'); cy.get(TIMELINES_FAVORITE).last().should('exist'); cy.get(TIMELINE_TITLE).should('have.text', getTimeline().title); - cy.get(TIMELINE_DESCRIPTION).should('have.text', getTimeline().description); }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts index ba20f89defef7..80d4a9780c6e7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/row_renderers.cy.ts @@ -16,7 +16,8 @@ import { TIMELINE_ROW_RENDERERS_SURICATA_LINK_TOOLTIP, TIMELINE_ROW_RENDERERS_MODAL_CLOSE_BUTTON, } from '../../../screens/timeline'; -import { deleteTimelines, waitForWelcomePanelToBeLoaded } from '../../../tasks/common'; +import { deleteTimelines } from '../../../tasks/api_calls/common'; +import { waitForWelcomePanelToBeLoaded } from '../../../tasks/common'; import { waitForAllHostsToBeLoaded } from '../../../tasks/hosts/all_hosts'; import { login } from '../../../tasks/login'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts index 00b3726bfec21..a7b4191d8b470 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/search_or_filter.cy.ts @@ -22,6 +22,7 @@ import { changeTimelineQueryLanguage, executeTimelineKQL, executeTimelineSearch, + showDataProviderQueryBuilder, } from '../../../tasks/timeline'; import { waitForTimelinesPanelToBeLoaded } from '../../../tasks/timelines'; @@ -52,7 +53,8 @@ describe('Timeline search and filters', { tags: ['@ess', '@serverless'] }, () => }); }); - describe('Update kqlMode for timeline', () => { + // FLAKY: https://github.com/elastic/kibana/issues/169882 + describe.skip('Update kqlMode for timeline', () => { beforeEach(() => { login(); visit(TIMELINES_URL); @@ -60,6 +62,7 @@ describe('Timeline search and filters', { tags: ['@ess', '@serverless'] }, () => openTimelineUsingToggle(); cy.intercept('PATCH', '/api/timeline').as('update'); cy.get(LOADING_INDICATOR).should('not.exist'); + showDataProviderQueryBuilder(); cy.get(TIMELINE_SEARCH_OR_FILTER).click(); cy.get(TIMELINE_SEARCH_OR_FILTER).should('exist'); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts index 780beec43d838..fd52a80ed9ff6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/unsaved_timeline.cy.ts @@ -20,7 +20,10 @@ import { } from '../../../tasks/kibana_navigation'; import { login } from '../../../tasks/login'; import { visitWithTimeRange } from '../../../tasks/navigation'; -import { closeTimelineUsingToggle } from '../../../tasks/security_main'; +import { + closeTimelineUsingCloseButton, + openTimelineUsingToggle, +} from '../../../tasks/security_main'; import { navigateToHostsUsingBreadcrumb, navigateToExploreUsingBreadcrumb, @@ -35,7 +38,8 @@ import { } from '../../../tasks/timeline'; import { EXPLORE_URL, hostsUrl, MANAGE_URL } from '../../../urls/navigation'; -describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/169588 +describe.skip('Save Timeline Prompts', { tags: ['@ess'] }, () => { before(() => { login(); /* @@ -52,6 +56,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { beforeEach(() => { login(); visitWithTimeRange(hostsUrl('allHosts')); + openTimelineUsingToggle(); createNewTimeline(); }); @@ -63,7 +68,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('Changed & unsaved timeline should prompt when user navigates away from security solution', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); openKibanaNavigation(); navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); @@ -72,7 +77,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('Changed & unsaved timeline should NOT prompt when user navigates away within security solution where timelines are enabled', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); // navigate to any other page in security solution openKibanaNavigation(); cy.get(CASES_PAGE).click(); @@ -90,7 +95,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('Changed & saved timeline should NOT prompt when user navigates away out of security solution', () => { populateTimeline(); addNameToTimelineAndSave('Test'); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); openKibanaNavigation(); navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE); cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); @@ -99,7 +104,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('Changed & saved timeline should NOT prompt when user navigates within security solution where timelines are disabled', () => { populateTimeline(); addNameToTimelineAndSave('Test'); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); openKibanaNavigation(); cy.get(MANAGE_PAGE).click(); cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); @@ -107,7 +112,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('When user navigates to the page where timeline is present, Timeline save modal should not exists.', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); openKibanaNavigation(); cy.get(MANAGE_PAGE).click(); cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); @@ -123,7 +128,7 @@ describe('Save Timeline Prompts', { tags: ['@ess'] }, () => { it('Changed and unsaved timeline should NOT prompt when user navigates from the page where timeline is disabled', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); openKibanaNavigation(); cy.get(MANAGE_PAGE).click(); cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); @@ -157,11 +162,12 @@ describe('Save Timeline Prompts', { tags: ['@serverless'] }, () => { beforeEach(() => { login(); visitWithTimeRange(hostsUrl('allHosts')); + openTimelineUsingToggle(); createNewTimeline(); }); it('unchanged & unsaved timeline should NOT prompt when it is closed and navigate to any page', () => { - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); navigateToAlertsPageInServerless(); // security page with timelines enabled cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); @@ -173,7 +179,7 @@ describe('Save Timeline Prompts', { tags: ['@serverless'] }, () => { it('Changed & unsaved timeline should prompt when it is closed and navigate to Security page without timeline', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); navigateToAlertsPageInServerless(); // security page with timelines enabled cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); @@ -184,7 +190,7 @@ describe('Save Timeline Prompts', { tags: ['@serverless'] }, () => { it('Changed & unsaved timeline should prompt when it is closed and navigate to external page', () => { populateTimeline(); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); navigateToDiscoverPageInServerless(); cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible'); @@ -194,7 +200,7 @@ describe('Save Timeline Prompts', { tags: ['@serverless'] }, () => { it('Changed & saved timeline should NOT prompt when it is closed', () => { populateTimeline(); addNameToTimelineAndSave('Test'); - closeTimelineUsingToggle(); + closeTimelineUsingCloseButton(); navigateToAlertsPageInServerless(); // security page with timelines enabled cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist'); diff --git a/x-pack/test/security_solution_cypress/cypress/objects/exception.ts b/x-pack/test/security_solution_cypress/cypress/objects/exception.ts index 6e754d837dcf5..6865784ae5bab 100644 --- a/x-pack/test/security_solution_cypress/cypress/objects/exception.ts +++ b/x-pack/test/security_solution_cypress/cypress/objects/exception.ts @@ -6,6 +6,7 @@ */ import type { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ELASTICSEARCH_USERNAME } from '../env_var_names_constants'; export interface Exception { field: string; @@ -61,5 +62,15 @@ export const expectedExportedExceptionList = ( exceptionListResponse: Cypress.Response ): string => { const jsonRule = exceptionListResponse.body; - return `{"_version":"${jsonRule._version}","created_at":"${jsonRule.created_at}","created_by":"system_indices_superuser","description":"${jsonRule.description}","id":"${jsonRule.id}","immutable":false,"list_id":"${jsonRule.list_id}","name":"${jsonRule.name}","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"${jsonRule.tie_breaker_id}","type":"${jsonRule.type}","updated_at":"${jsonRule.updated_at}","updated_by":"system_indices_superuser","version":1}\n{"exported_exception_list_count":1,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n`; + return `{"_version":"${jsonRule._version}","created_at":"${ + jsonRule.created_at + }","created_by":"${Cypress.env(ELASTICSEARCH_USERNAME)}","description":"${ + jsonRule.description + }","id":"${jsonRule.id}","immutable":false,"list_id":"${jsonRule.list_id}","name":"${ + jsonRule.name + }","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"${ + jsonRule.tie_breaker_id + }","type":"${jsonRule.type}","updated_at":"${jsonRule.updated_at}","updated_by":"${Cypress.env( + ELASTICSEARCH_USERNAME + )}","version":1}\n{"exported_exception_list_count":1,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n`; }; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/entity_analytics_management.ts b/x-pack/test/security_solution_cypress/cypress/screens/entity_analytics_management.ts index ebdabcb67bb1e..2c025efcbe79a 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/entity_analytics_management.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/entity_analytics_management.ts @@ -33,4 +33,9 @@ export const RISK_SCORE_UPDATE_BUTTON = '[data-test-subj="risk-score-update-butt export const RISK_SCORE_STATUS = '[data-test-subj="risk-score-status"]'; +export const RISK_SCORE_STATUS_LOADING = '[data-test-subj="risk-score-status-loading"]'; + +export const RISK_SCORE_PRIVILEGES_CALLOUT = + '[data-test-subj="callout-missing-risk-engine-privileges"]'; + export const RISK_SCORE_SWITCH = '[data-test-subj="risk-score-switch"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts index 755c31d116a63..35ed8f63b216b 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts @@ -29,7 +29,7 @@ export const CORRELATION_EVENT_TABLE_CELL = export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; -export const COMBO_BOX = '.euiComboBoxOption__content'; +export const COMBO_BOX = 'button.euiFilterSelectItem[role="option"]'; export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]'; @@ -222,7 +222,7 @@ export const TIMELINE_FLYOUT_BODY = '[data-test-subj="query-tab-flyout-body"]'; export const TIMELINE_HEADER = '[data-test-subj="timeline-hide-show-container"]'; -export const TIMELINE_INSPECT_BUTTON = `${TIMELINE_FLYOUT} [data-test-subj="inspect-icon-button"]`; +export const TIMELINE_INSPECT_BUTTON = `${TIMELINE_FLYOUT} [data-test-subj="inspect-empty-button"]`; export const TIMELINE_PANEL = `[data-test-subj="timeline-flyout-header-panel"]`; @@ -261,6 +261,8 @@ export const TIMELINE_SAVE_MODAL = '[data-test-subj="save-timeline-modal"]'; export const TIMELINE_EDIT_MODAL_SAVE_BUTTON = '[data-test-subj="save-button"]'; +export const TIMELINE_EDIT_MODAL_SAVE_AS_NEW_SWITCH = '[data-test-subj="save-as-new-switch"]'; + export const TIMELINE_EXIT_FULL_SCREEN_BUTTON = '[data-test-subj="exit-full-screen"]'; export const TIMELINE_FLYOUT_WRAPPER = '[data-test-subj="flyout-pane"]'; @@ -354,3 +356,14 @@ export const OPEN_TIMELINE_MODAL_SEARCH_BAR = `${OPEN_TIMELINE_MODAL} ${getData export const OPEN_TIMELINE_MODAL_TIMELINE_NAMES = `${OPEN_TIMELINE_MODAL} ${getDataTestSubjectSelectorStartWith( 'timeline-title-' )}`; + +export const TIMELINE_FILTER_BADGE = `[data-test-subj^='timeline-filters-container'] [data-test-subj^="filter-badge"]`; + +export const NEW_TIMELINE_ACTION = getDataTestSubjectSelector('new-timeline-action'); + +export const SAVE_TIMELINE_ACTION = getDataTestSubjectSelector('save-timeline-action'); +export const SAVE_TIMELINE_ACTION_BTN = getDataTestSubjectSelector('save-timeline-action-btn'); + +export const SAVE_TIMELINE_TOOLTIP = getDataTestSubjectSelector('save-timeline-btn-tooltip'); + +export const TOGGLE_DATA_PROVIDER_BTN = getDataTestSubjectSelector('toggle-data-provider'); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts b/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts index b7d12bca8b744..a90faeb6e80f3 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timelines.ts @@ -59,3 +59,5 @@ export const TIMELINES_OVERVIEW_ONLY_FAVORITES = `${TIMELINES_OVERVIEW} [data-te export const TIMELINES_OVERVIEW_SEARCH = `${TIMELINES_OVERVIEW} [data-test-subj="search-bar"]`; export const TIMELINES_OVERVIEW_TABLE = `${TIMELINES_OVERVIEW} [data-test-subj="timelines-table"]`; + +export const ROWS = '.euiTableRow'; diff --git a/x-pack/test/security_solution_cypress/cypress/support/setup_users.ts b/x-pack/test/security_solution_cypress/cypress/support/setup_users.ts index e1dc4c952eac7..02ebebb6c10ea 100644 --- a/x-pack/test/security_solution_cypress/cypress/support/setup_users.ts +++ b/x-pack/test/security_solution_cypress/cypress/support/setup_users.ts @@ -6,7 +6,7 @@ */ import { Role } from '@kbn/security-plugin/common'; -import { rootRequest } from '../tasks/common'; +import { rootRequest } from '../tasks/api_calls/common'; /** * Utility function creates roles and corresponding users per each role with names diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/alerts.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/alerts.ts index 43d9952b9b376..3b9c0612a0724 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/alerts.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/alerts.ts @@ -9,7 +9,7 @@ import { RuleObjectId, RuleSignatureId, } from '@kbn/security-solution-plugin/common/api/detection_engine'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const DEFAULT_ALERTS_INDEX_PATTERN = '.alerts-security.alerts-*'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/common.ts new file mode 100644 index 0000000000000..775b4c5a8964b --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/common.ts @@ -0,0 +1,264 @@ +/* + * Copyright 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 { DATA_VIEW_PATH, INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { ELASTICSEARCH_PASSWORD, ELASTICSEARCH_USERNAME } from '../../env_var_names_constants'; +import { deleteAllDocuments } from './elasticsearch'; +import { DEFAULT_ALERTS_INDEX_PATTERN } from './alerts'; + +export const API_AUTH = Object.freeze({ + user: Cypress.env(ELASTICSEARCH_USERNAME), + pass: Cypress.env(ELASTICSEARCH_PASSWORD), +}); + +export const API_HEADERS = Object.freeze({ + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + [ELASTIC_HTTP_VERSION_HEADER]: [INITIAL_REST_VERSION], +}); + +export const rootRequest = ({ + headers: optionHeaders, + ...restOptions +}: Partial): Cypress.Chainable> => + cy.request({ + auth: API_AUTH, + headers: { + ...API_HEADERS, + ...(optionHeaders || {}), + }, + ...restOptions, + }); + +export const deleteAlertsAndRules = () => { + cy.log('Delete all alerts and rules'); + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + + rootRequest({ + method: 'POST', + url: '/api/detection_engine/rules/_bulk_action', + body: { + query: '', + action: 'delete', + }, + failOnStatusCode: false, + timeout: 300000, + }); + + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'alert', + }, + }, + ], + }, + }, + }, + }); + + deleteAllDocuments(`.lists-*,.items-*,${DEFAULT_ALERTS_INDEX_PATTERN}`); +}; + +export const deleteExceptionLists = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'exception-list', + }, + }, + ], + }, + }, + }, + }); +}; + +export const deleteEndpointExceptionList = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'exception-list-agnostic', + }, + }, + ], + }, + }, + }, + }); +}; + +export const deleteTimelines = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'siem-ui-timeline', + }, + }, + ], + }, + }, + }, + }); +}; + +export const deleteAlertsIndex = () => { + rootRequest({ + method: 'POST', + url: '/api/index_management/indices/delete', + body: { indices: ['.internal.alerts-security.alerts-default-000001'] }, + failOnStatusCode: false, + }); +}; + +export const deleteAllCasesItems = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_alerting_cases_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + bool: { + should: [ + { + term: { + type: 'cases', + }, + }, + { + term: { + type: 'cases-configure', + }, + }, + { + term: { + type: 'cases-comments', + }, + }, + { + term: { + type: 'cases-user-action', + }, + }, + { + term: { + type: 'cases-connector-mappings', + }, + }, + ], + }, + }, + ], + }, + }, + }, + }); +}; + +export const deleteConnectors = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_alerting_cases_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'action', + }, + }, + ], + }, + }, + }, + }); +}; + +export const deletePrebuiltRulesAssets = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'security-rule', + }, + }, + ], + }, + }, + }, + }); +}; + +export const postDataView = (indexPattern: string, name?: string, id?: string) => { + rootRequest({ + method: 'POST', + url: DATA_VIEW_PATH, + body: { + data_view: { + id: id || indexPattern, + name: name || indexPattern, + fieldAttrs: '{}', + title: indexPattern, + timeFieldName: '@timestamp', + }, + }, + failOnStatusCode: false, + }); +}; + +export const deleteDataView = (dataViewId: string) => { + rootRequest({ + method: 'POST', + url: 'api/content_management/rpc/delete', + body: { + contentTypeId: 'index-pattern', + id: dataViewId, + options: { force: true }, + version: 1, + }, + failOnStatusCode: false, + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/elasticsearch.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/elasticsearch.ts index d94049f14c8a1..6dc3622f72a03 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/elasticsearch.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/elasticsearch.ts @@ -4,17 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const deleteIndex = (index: string) => { rootRequest({ method: 'DELETE', url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); }; @@ -23,11 +18,6 @@ export const deleteDataStream = (dataStreamName: string) => { rootRequest({ method: 'DELETE', url: `${Cypress.env('ELASTICSEARCH_URL')}/_data_stream/${dataStreamName}`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); }; @@ -40,11 +30,6 @@ export const deleteAllDocuments = (target: string) => { url: `${Cypress.env( 'ELASTICSEARCH_URL' )}/${target}/_delete_by_query?conflicts=proceed&scroll_size=10000&refresh`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, body: { query: { match_all: {}, @@ -57,11 +42,6 @@ export const createIndex = (indexName: string, properties: Record({ method: 'GET', url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }).then((response) => { if (response.status !== 200) { @@ -110,11 +80,6 @@ export const refreshIndex = (index: string) => { rootRequest({ method: 'POST', url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_refresh`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }).then((response) => { if (response.status !== 200) { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts index 8b7d85b7ebf4d..4e0afa7416da0 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts @@ -13,17 +13,12 @@ import type { } from '@kbn/securitysolution-io-ts-list-types'; import { ENDPOINT_LIST_ITEM_URL, ENDPOINT_LIST_URL } from '@kbn/securitysolution-list-constants'; import type { ExceptionList, ExceptionListItem, RuleExceptionItem } from '../../objects/exception'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const createEndpointExceptionList = () => rootRequest({ method: 'POST', url: ENDPOINT_LIST_URL, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); export const createEndpointExceptionListItem = (item: CreateEndpointListItemSchema) => @@ -31,11 +26,6 @@ export const createEndpointExceptionListItem = (item: CreateEndpointListItemSche method: 'POST', url: ENDPOINT_LIST_ITEM_URL, body: item, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); export const createExceptionList = ( @@ -51,11 +41,6 @@ export const createExceptionList = ( name: exceptionList.name, type: exceptionList.type, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); @@ -88,11 +73,6 @@ export const createExceptionListItem = ( ], expire_time: exceptionListItem?.expire_time, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); @@ -103,11 +83,6 @@ export const createRuleExceptionItem = (ruleId: string, exceptionListItems: Rule body: { items: exceptionListItems, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); @@ -122,11 +97,6 @@ export const updateExceptionListItem = ( item_id: exceptionListItemId, ...exceptionListItemUpdate, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); @@ -134,10 +104,5 @@ export const deleteExceptionList = (listId: string, namespaceType: string) => rootRequest({ method: 'DELETE', url: `/api/exception_lists?list_id=${listId}&namespace_type=${namespaceType}`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/fleet.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/fleet.ts index 5295b033fc7cf..d77361c95ebdd 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/fleet.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/fleet.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { rootRequest } from '../common'; +import { rootRequest } from './common'; /** * Deletes all existing Fleet packages, package policies and agent policies. @@ -25,21 +25,11 @@ const deleteAgentPolicies = () => { return rootRequest<{ items: Array<{ id: string }> }>({ method: 'GET', url: 'api/fleet/agent_policies', - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }).then((response) => { response.body.items.forEach((item: { id: string }) => { rootRequest({ method: 'POST', url: `api/fleet/agent_policies/delete`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, body: { agentPolicyId: item.id, }, @@ -52,20 +42,10 @@ const deletePackagePolicies = () => { return rootRequest<{ items: Array<{ id: string }> }>({ method: 'GET', url: 'api/fleet/package_policies', - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }).then((response) => { rootRequest({ method: 'POST', url: `api/fleet/package_policies/delete`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, body: { packagePolicyIds: response.body.items.map((item: { id: string }) => item.id), }, @@ -77,22 +57,12 @@ const deletePackages = () => { return rootRequest<{ items: Array<{ status: string; name: string; version: string }> }>({ method: 'GET', url: 'api/fleet/epm/packages', - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }).then((response) => { response.body.items.forEach((item) => { if (item.status === 'installed') { rootRequest({ method: 'DELETE', url: `api/fleet/epm/packages/${item.name}/${item.version}`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); } }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/integrations.ts similarity index 83% rename from x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts rename to x-pack/test/security_solution_cypress/cypress/tasks/api_calls/integrations.ts index eeef97682b9c7..4e212d4b8eb34 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/integrations.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/integrations.ts @@ -56,11 +56,6 @@ export function installIntegrations({ packages, force: true, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); // Install agent and package policies @@ -68,11 +63,6 @@ export function installIntegrations({ method: 'POST', url: `${AGENT_POLICY_API_ROUTES.CREATE_PATTERN}?sys_monitoring=true`, body: agentPolicy, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }).then((response) => { const packagePolicyWithAgentPolicyId: PackagePolicy = { ...packagePolicy, @@ -83,11 +73,6 @@ export function installIntegrations({ method: 'POST', url: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN, body: packagePolicyWithAgentPolicyId, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/kibana_advanced_settings.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/kibana_advanced_settings.ts index 2c982adad5275..27d5063ce30df 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/kibana_advanced_settings.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/kibana_advanced_settings.ts @@ -7,14 +7,13 @@ import { SECURITY_SOLUTION_SHOW_RELATED_INTEGRATIONS_ID } from '@kbn/management-settings-ids'; import { ENABLE_EXPANDABLE_FLYOUT_SETTING } from '@kbn/security-solution-plugin/common/constants'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const setKibanaSetting = (key: string, value: boolean | number | string) => { rootRequest({ method: 'POST', url: 'internal/kibana/settings', body: { changes: { [key]: value } }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, }); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/machine_learning.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/machine_learning.ts index afb468876c5f1..f03d6edadbc18 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/machine_learning.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/machine_learning.ts @@ -7,17 +7,12 @@ import { ML_INTERNAL_BASE_PATH } from '@kbn/ml-plugin/common/constants/app'; import type { Module } from '@kbn/ml-plugin/common/types/modules'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const fetchMachineLearningModules = () => { return rootRequest({ method: 'GET', url: `${ML_INTERNAL_BASE_PATH}/modules/get_module`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': 1, - }, failOnStatusCode: false, }); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/notes.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/notes.ts index d1addc0407900..21f718dc355d1 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/notes.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/notes.ts @@ -17,5 +17,9 @@ export const addNoteToTimeline = ( version: null, note: { note, timelineId }, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts index 98b9a884e683a..273491ebd2efe 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts @@ -13,17 +13,12 @@ import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common/d import type { PrePackagedRulesStatusResponse } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; import { getPrebuiltRuleWithExceptionsMock } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks'; import { createRuleAssetSavedObject } from '../../helpers/rules'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const getPrebuiltRulesStatus = () => { return rootRequest({ method: 'GET', url: 'api/detection_engine/rules/prepackaged/_status', - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); }; @@ -43,14 +38,12 @@ export const installAllPrebuiltRulesRequest = () => rootRequest({ method: 'POST', url: PERFORM_RULE_INSTALLATION_URL, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '1', - }, body: { mode: 'ALL_RULES', }, + headers: { + 'elastic-api-version': '1', + }, }); /* Install specific prebuilt rules. Should be available as security-rule saved objects @@ -63,11 +56,6 @@ export const installSpecificPrebuiltRulesRequest = (rules: Array({ method: 'POST', url: PERFORM_RULE_INSTALLATION_URL, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '1', - }, body: { mode: 'SPECIFIC_RULES', rules: rules.map((rule) => ({ @@ -75,6 +63,9 @@ export const installSpecificPrebuiltRulesRequest = (rules: Array { @@ -128,8 +119,6 @@ export const createNewRuleAsset = ({ method: 'PUT', url, headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', 'Content-Type': 'application/json', }, failOnStatusCode: false, @@ -182,7 +171,7 @@ export const bulkCreateRuleAssets = ({ return rootRequest({ method: 'POST', url, - headers: { 'kbn-xsrf': 'cypress-creds', 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json' }, failOnStatusCode: false, body: bulkIndexRequestBody, }).then((response) => response.status === 200); @@ -197,8 +186,6 @@ export const getRuleAssets = (index: string | undefined = '.kibana_security_solu method: 'GET', url, headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', 'Content-Type': 'application/json', }, failOnStatusCode: false, diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/rules.ts index 254d93cac5078..5f89e57ff81c6 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/rules.ts @@ -16,16 +16,11 @@ import type { } from '@kbn/security-solution-plugin/common/api/detection_engine'; import type { FetchRulesResponse } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; import { internalAlertingSnoozeRule } from '../../urls/routes'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const findAllRules = () => { return rootRequest({ url: DETECTION_ENGINE_RULES_URL_FIND, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); }; @@ -36,11 +31,6 @@ export const createRule = ( method: 'POST', url: DETECTION_ENGINE_RULES_URL, body: rule, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); }; @@ -52,7 +42,7 @@ export const createRule = ( * @param duration Snooze duration in milliseconds, -1 for indefinite */ export const snoozeRule = (id: string, duration: number): Cypress.Chainable => - cy.request({ + rootRequest({ method: 'POST', url: internalAlertingSnoozeRule(id), body: { @@ -61,7 +51,6 @@ export const snoozeRule = (id: string, duration: number): Cypress.Chainable => rRule: { dtstart: new Date().toISOString(), count: 1, tzid: moment().format('zz') }, }, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, failOnStatusCode: false, }); @@ -69,11 +58,6 @@ export const deleteCustomRule = (ruleId = '1') => { rootRequest({ method: 'DELETE', url: `api/detection_engine/rules?rule_id=${ruleId}`, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, failOnStatusCode: false, }); }; @@ -89,10 +73,7 @@ export const importRule = (ndjsonPath: string) => { url: 'api/detection_engine/rules/_import', method: 'POST', headers: { - 'kbn-xsrf': 'cypress-creds', 'content-type': 'multipart/form-data', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', }, body: formdata, }) @@ -108,10 +89,7 @@ export const waitForRulesToFinishExecution = (ruleIds: string[], afterDate?: Dat method: 'GET', url: DETECTION_ENGINE_RULES_URL_FIND, headers: { - 'kbn-xsrf': 'cypress-creds', 'content-type': 'multipart/form-data', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', }, }).then((response) => { const areAllRulesFinished = ruleIds.every((ruleId) => diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/saved_queries.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/saved_queries.ts index 88049b20d6d5b..0ec356e83727c 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/saved_queries.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/saved_queries.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { SavedQuery } from '@kbn/data-plugin/public'; import { SAVED_QUERY_BASE_URL } from '@kbn/data-plugin/common/constants'; -import { rootRequest } from '../common'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import { rootRequest } from './common'; export const createSavedQuery = ( title: string, @@ -37,9 +37,7 @@ export const createSavedQuery = ( ], }, headers: { - 'kbn-xsrf': 'cypress-creds', [ELASTIC_HTTP_VERSION_HEADER]: '1', - 'x-elastic-internal-origin': 'security-solution', }, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/sourcerer.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/sourcerer.ts new file mode 100644 index 0000000000000..a266678ee95d9 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/sourcerer.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 { rootRequest } from './common'; + +export const deleteRuntimeField = (dataView: string, fieldName: string) => { + const deleteRuntimeFieldPath = `/api/data_views/data_view/${dataView}/runtime_field/${fieldName}`; + + rootRequest({ + url: deleteRuntimeFieldPath, + method: 'DELETE', + failOnStatusCode: false, + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts index 9b6a0c98db40c..620a105a2b98e 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/timelines.ts @@ -7,7 +7,7 @@ import type { TimelineResponse } from '@kbn/security-solution-plugin/common/api/timeline'; import type { CompleteTimeline } from '../../objects/timeline'; -import { rootRequest } from '../common'; +import { rootRequest } from './common'; export const createTimeline = (timeline: CompleteTimeline) => rootRequest({ @@ -56,11 +56,6 @@ export const createTimeline = (timeline: CompleteTimeline) => : {}), }, }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'elastic-api-version': '2023-10-31', - }, }); export const createTimelineTemplate = (timeline: CompleteTimeline) => @@ -106,14 +101,23 @@ export const createTimelineTemplate = (timeline: CompleteTimeline) => savedQueryId: null, }, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, }); export const loadPrepackagedTimelineTemplates = () => rootRequest({ method: 'POST', url: 'api/timeline/_prepackaged', - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + + 'elastic-api-version': '2023-10-31', + }, }); export const favoriteTimeline = ({ @@ -136,5 +140,4 @@ export const favoriteTimeline = ({ templateTimelineId: templateTimelineId || null, templateTimelineVersion: templateTimelineVersion || null, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index a9b019fc1f6f6..b7d0062cd5d02 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -5,13 +5,8 @@ * 2.0. */ -import { DATA_VIEW_PATH, INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { KIBANA_LOADING_ICON } from '../screens/security_header'; import { EUI_BASIC_TABLE_LOADING } from '../screens/common/controls'; -import { deleteAllDocuments } from './api_calls/elasticsearch'; -import { DEFAULT_ALERTS_INDEX_PATTERN } from './api_calls/alerts'; -import { ELASTICSEARCH_PASSWORD, ELASTICSEARCH_USERNAME } from '../env_var_names_constants'; const primaryButton = 0; @@ -21,30 +16,6 @@ const primaryButton = 0; */ const dndSloppyClickDetectionThreshold = 5; -export const API_AUTH = Object.freeze({ - user: Cypress.env(ELASTICSEARCH_USERNAME), - pass: Cypress.env(ELASTICSEARCH_PASSWORD), -}); - -export const API_HEADERS = Object.freeze({ - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - [ELASTIC_HTTP_VERSION_HEADER]: [INITIAL_REST_VERSION], -}); - -export const rootRequest = ({ - headers: optionHeaders, - ...restOptions -}: Partial): Cypress.Chainable> => - cy.request({ - auth: API_AUTH, - headers: { - ...API_HEADERS, - ...(optionHeaders || {}), - }, - ...restOptions, - }); - /** Starts dragging the subject */ export const drag = (subject: JQuery) => { const subjectLocation = subject[0].getBoundingClientRect(); @@ -99,243 +70,6 @@ export const resetRulesTableState = () => { clearSessionStorage(); }; -export const deleteAlertsAndRules = () => { - cy.log('Delete all alerts and rules'); - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - - rootRequest({ - method: 'POST', - url: '/api/detection_engine/rules/_bulk_action', - body: { - query: '', - action: 'delete', - }, - failOnStatusCode: false, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - }, - timeout: 300000, - }); - - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'alert', - }, - }, - ], - }, - }, - }, - }); - - deleteAllDocuments(`.lists-*,.items-*,${DEFAULT_ALERTS_INDEX_PATTERN}`); -}; - -export const deleteExceptionLists = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'exception-list', - }, - }, - ], - }, - }, - }, - }); -}; - -export const deleteEndpointExceptionList = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'exception-list-agnostic', - }, - }, - ], - }, - }, - }, - }); -}; - -export const deleteTimelines = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'siem-ui-timeline', - }, - }, - ], - }, - }, - }, - }); -}; - -export const deleteAlertsIndex = () => { - rootRequest({ - method: 'POST', - url: '/api/index_management/indices/delete', - body: { indices: ['.internal.alerts-security.alerts-default-000001'] }, - failOnStatusCode: false, - }); -}; - -export const deleteAllCasesItems = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_alerting_cases_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - bool: { - should: [ - { - term: { - type: 'cases', - }, - }, - { - term: { - type: 'cases-configure', - }, - }, - { - term: { - type: 'cases-comments', - }, - }, - { - term: { - type: 'cases-user-action', - }, - }, - { - term: { - type: 'cases-connector-mappings', - }, - }, - ], - }, - }, - ], - }, - }, - }, - }); -}; - -export const deleteConnectors = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_alerting_cases_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'action', - }, - }, - ], - }, - }, - }, - }); -}; - -export const deletePrebuiltRulesAssets = () => { - const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; - rootRequest({ - method: 'POST', - url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, - body: { - query: { - bool: { - filter: [ - { - match: { - type: 'security-rule', - }, - }, - ], - }, - }, - }, - }); -}; - -export const postDataView = (indexPattern: string, name?: string, id?: string) => { - rootRequest({ - method: 'POST', - url: DATA_VIEW_PATH, - body: { - data_view: { - id: id || indexPattern, - name: name || indexPattern, - fieldAttrs: '{}', - title: indexPattern, - timeFieldName: '@timestamp', - }, - }, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - }, - failOnStatusCode: false, - }); -}; - -export const deleteDataView = (dataViewId: string) => { - rootRequest({ - method: 'POST', - url: 'api/content_management/rpc/delete', - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, - body: { - contentTypeId: 'index-pattern', - id: dataViewId, - options: { force: true }, - version: 1, - }, - failOnStatusCode: false, - }); -}; - export const scrollToBottom = () => cy.scrollTo('bottom'); export const waitForWelcomePanelToBeLoaded = () => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/edit_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/edit_rule.ts index 0f3d9ee86529d..14c9ef05aa878 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/edit_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/edit_rule.ts @@ -5,13 +5,12 @@ * 2.0. */ -import type { SecurityRoleName } from '@kbn/security-solution-plugin/common/test'; import { BACK_TO_RULE_DETAILS, EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; import { editRuleUrl } from '../urls/edit_rule'; import { visit } from './navigation'; -export function visitEditRulePage(ruleId: string, role?: SecurityRoleName): void { - visit(editRuleUrl(ruleId), { role }); +export function visitEditRulePage(ruleId: string): void { + visit(editRuleUrl(ruleId)); } export const saveEditedRule = () => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions_table.ts b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions_table.ts index c3af909131cdf..710526bdff18d 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions_table.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions_table.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { INPUT_FILE, TOASTER, TOASTER_BODY } from '../screens/alerts_detection_rules'; +import { + INPUT_FILE, + TOASTER, + TOASTER_BODY, + TOASTER_CLOSE_ICON, +} from '../screens/alerts_detection_rules'; import { EXCEPTIONS_TABLE, EXCEPTIONS_TABLE_SEARCH, @@ -70,6 +75,12 @@ export const exportExceptionList = (listId: string) => { .click(); cy.get(EXCEPTIONS_TABLE_EXPORT_MODAL_BTN).first().click(); cy.get(EXCEPTIONS_TABLE_EXPIRED_EXCEPTION_ITEMS_MODAL_CONFIRM_BTN).first().click(); + cy.root().then(($page) => { + const element = $page.find(TOASTER_CLOSE_ICON); + if (element.length > 0) { + closeErrorToast(); + } + }); }; export const assertNumberLinkedRules = (listId: string, numberOfRulesAsString: string) => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/login.ts b/x-pack/test/security_solution_cypress/cypress/tasks/login.ts index 4bf71413f8a57..884c0a656df56 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/login.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/login.ts @@ -6,19 +6,17 @@ */ import * as yaml from 'js-yaml'; -import type { UrlObject } from 'url'; -import Url from 'url'; import { LoginState } from '@kbn/security-plugin/common/login_state'; import type { SecurityRoleName } from '@kbn/security-solution-plugin/common/test'; import { KNOWN_SERVERLESS_ROLE_DEFINITIONS } from '@kbn/security-solution-plugin/common/test'; import { LOGOUT_URL } from '../urls/navigation'; -import { API_HEADERS, rootRequest } from './common'; import { CLOUD_SERVERLESS, ELASTICSEARCH_PASSWORD, ELASTICSEARCH_USERNAME, IS_SERVERLESS, } from '../env_var_names_constants'; +import { API_HEADERS, rootRequest } from './api_calls/common'; /** * Credentials in the `kibana.dev.yml` config file will be used to authenticate @@ -65,29 +63,6 @@ export const loginWithUser = (user: User): void => { loginWithUsernameAndPassword(user.username, user.password); }; -/** - * 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 - * directly from the environment variables set up in x-pack/test/security_solution_cypress/runner.ts - * - * @param role string role/user to log in with - * @param route string route to visit - */ -export const getUrlWithRoute = (role: SecurityRoleName, route: string): string => { - const url = Cypress.config().baseUrl; - const kibana = new URL(String(url)); - const theUrl = `${Url.format({ - auth: `${role}:changeme`, - username: role, - password: 'changeme', - protocol: kibana.protocol.replace(':', ''), - hostname: kibana.hostname, - port: kibana.port, - } as UrlObject)}${route.startsWith('/') ? '' : '/'}${route}`; - cy.log(`origin: ${theUrl}`); - return theUrl; -}; - /** * Builds a URL with basic auth using the passed in user. * diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/navigation.ts b/x-pack/test/security_solution_cypress/cypress/tasks/navigation.ts index b7c6f55386c3f..47914bbf75521 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/navigation.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/navigation.ts @@ -8,34 +8,24 @@ import { encode } from '@kbn/rison'; import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants'; -import type { SecurityRoleName } from '@kbn/security-solution-plugin/common/test'; import { GET_STARTED_URL, hostDetailsUrl, userDetailsUrl } from '../urls/navigation'; -import { constructUrlWithUser, getUrlWithRoute, User } from './login'; export const visit = ( url: string, options?: { visitOptions?: Partial; - role?: SecurityRoleName; } ) => { - cy.visit(options?.role ? getUrlWithRoute(options.role, url) : url, { + cy.visit(url, { onBeforeLoad: disableNewFeaturesTours, ...options?.visitOptions, }); }; -export const visitWithUser = (url: string, user: User) => { - cy.visit(constructUrlWithUser(user, url), { - onBeforeLoad: disableNewFeaturesTours, - }); -}; - export const visitWithTimeRange = ( url: string, options?: { visitOptions?: Partial; - role?: SecurityRoleName; } ) => { const timerangeConfig = { @@ -57,7 +47,7 @@ export const visitWithTimeRange = ( }, }); - cy.visit(options?.role ? getUrlWithRoute(options.role, url) : url, { + cy.visit(url, { ...options, qs: { ...options?.visitOptions?.qs, @@ -74,9 +64,9 @@ export const visitWithTimeRange = ( }); }; -export const visitTimeline = (timelineId: string, role?: SecurityRoleName) => { +export const visitTimeline = (timelineId: string) => { const route = `/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`; - cy.visit(role ? getUrlWithRoute(role, route) : route, { + cy.visit(route, { onBeforeLoad: disableNewFeaturesTours, }); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts index 1dadb67a96987..f9dd302d5956c 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts @@ -51,7 +51,7 @@ interface VisitRuleDetailsPageOptions { } export function visitRuleDetailsPage(ruleId: string, options?: VisitRuleDetailsPageOptions): void { - visit(ruleDetailsUrl(ruleId, options?.tab), { role: options?.role }); + visit(ruleDetailsUrl(ruleId, options?.tab)); } export const enablesRule = () => { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rules_management.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rules_management.ts index 663692aa905d4..79af9992b9c12 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/rules_management.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rules_management.ts @@ -5,15 +5,14 @@ * 2.0. */ -import type { SecurityRoleName } from '@kbn/security-solution-plugin/common/test'; import { LAST_BREADCRUMB, RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../screens/breadcrumbs'; import { RULES_MANAGEMENT_URL } from '../urls/rules_management'; import { resetRulesTableState } from './common'; import { visit } from './navigation'; -export function visitRulesManagementTable(role?: SecurityRoleName): void { +export function visitRulesManagementTable(): void { resetRulesTableState(); // Clear persistent rules filter data before page loading - visit(RULES_MANAGEMENT_URL, { role }); + visit(RULES_MANAGEMENT_URL); } export function openRuleManagementPageViaBreadcrumbs(): void { diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/security_main.ts b/x-pack/test/security_solution_cypress/cypress/tasks/security_main.ts index 9b8af6c5ceef6..15c171676c958 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/security_main.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/security_main.ts @@ -5,21 +5,13 @@ * 2.0. */ -import { - CLOSE_TIMELINE_BUTTON, - TIMELINE_TOGGLE_BUTTON, - TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON, -} from '../screens/security_main'; +import { CLOSE_TIMELINE_BUTTON, TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON } from '../screens/security_main'; import { TIMELINE_EXIT_FULL_SCREEN_BUTTON, TIMELINE_FULL_SCREEN_BUTTON } from '../screens/timeline'; export const openTimelineUsingToggle = () => { cy.get(TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON).click(); }; -export const closeTimelineUsingToggle = () => { - cy.get(TIMELINE_TOGGLE_BUTTON).filter(':visible').click(); -}; - export const closeTimelineUsingCloseButton = () => { cy.get(CLOSE_TIMELINE_BUTTON).filter(':visible').click(); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/sourcerer.ts b/x-pack/test/security_solution_cypress/cypress/tasks/sourcerer.ts index 39a6ed99b1b2e..a534fb3e6d3d8 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/sourcerer.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/sourcerer.ts @@ -9,7 +9,6 @@ import { DEFAULT_ALERTS_INDEX } from '@kbn/security-solution-plugin/common/const import { HOSTS_STAT, SOURCERER } from '../screens/sourcerer'; import { hostsUrl } from '../urls/navigation'; import { openTimelineUsingToggle } from './security_main'; -import { rootRequest } from './common'; import { visitWithTimeRange } from './navigation'; export const openSourcerer = (sourcererScope?: string) => { @@ -130,14 +129,3 @@ export const refreshUntilAlertsIndexExists = async () => { { interval: 500, timeout: 12000 } ); }; - -export const deleteRuntimeField = (dataView: string, fieldName: string) => { - const deleteRuntimeFieldPath = `/api/data_views/data_view/${dataView}/runtime_field/${fieldName}`; - - rootRequest({ - url: deleteRuntimeFieldPath, - method: 'DELETE', - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, - failOnStatusCode: false, - }); -}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index 2579e3794a073..ddf432a860922 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -54,8 +54,8 @@ import { CREATE_NEW_TIMELINE_TEMPLATE, OPEN_TIMELINE_TEMPLATE_ICON, TIMELINE_SAVE_MODAL, - TIMELINE_SAVE_MODAL_OPEN_BUTTON, TIMELINE_EDIT_MODAL_SAVE_BUTTON, + TIMELINE_EDIT_MODAL_SAVE_AS_NEW_SWITCH, TIMELINE_PROGRESS_BAR, QUERY_TAB_BUTTON, CLOSE_OPEN_TIMELINE_MODAL_BTN, @@ -89,6 +89,10 @@ import { OPEN_TIMELINE_MODAL_TIMELINE_NAMES, OPEN_TIMELINE_MODAL_SEARCH_BAR, OPEN_TIMELINE_MODAL, + NEW_TIMELINE_ACTION, + SAVE_TIMELINE_ACTION, + TOGGLE_DATA_PROVIDER_BTN, + SAVE_TIMELINE_ACTION_BTN, } from '../screens/timeline'; import { REFRESH_BUTTON, TIMELINE } from '../screens/timelines'; import { drag, drop } from './common'; @@ -102,7 +106,7 @@ export const addDescriptionToTimeline = ( modalAlreadyOpen: boolean = false ) => { if (!modalAlreadyOpen) { - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).first().click(); + cy.get(SAVE_TIMELINE_ACTION_BTN).first().click(); } cy.get(TIMELINE_DESCRIPTION_INPUT).should('not.be.disabled').type(description); cy.get(TIMELINE_DESCRIPTION_INPUT).invoke('val').should('equal', description); @@ -111,7 +115,7 @@ export const addDescriptionToTimeline = ( }; export const addNameToTimelineAndSave = (name: string) => { - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).first().click(); + cy.get(SAVE_TIMELINE_ACTION_BTN).first().click(); cy.get(TIMELINE_TITLE_INPUT).should('not.be.disabled').clear(); cy.get(TIMELINE_TITLE_INPUT).type(`${name}{enter}`); cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', name); @@ -119,12 +123,23 @@ export const addNameToTimelineAndSave = (name: string) => { cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); }; +export const addNameToTimelineAndSaveAsNew = (name: string) => { + cy.get(SAVE_TIMELINE_ACTION_BTN).first().click(); + cy.get(TIMELINE_TITLE_INPUT).should('not.be.disabled').clear(); + cy.get(TIMELINE_TITLE_INPUT).type(`${name}{enter}`); + cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', name); + cy.get(TIMELINE_EDIT_MODAL_SAVE_AS_NEW_SWITCH).should('exist'); + cy.get(TIMELINE_EDIT_MODAL_SAVE_AS_NEW_SWITCH).click(); + cy.get(TIMELINE_EDIT_MODAL_SAVE_BUTTON).click(); + cy.get(TIMELINE_TITLE_INPUT).should('not.exist'); +}; + export const addNameAndDescriptionToTimeline = ( timeline: Timeline, modalAlreadyOpen: boolean = false ) => { if (!modalAlreadyOpen) { - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).first().click(); + cy.get(SAVE_TIMELINE_ACTION).click(); } cy.get(TIMELINE_TITLE_INPUT).type(`${timeline.title}{enter}`); cy.get(TIMELINE_TITLE_INPUT).should('have.attr', 'value', timeline.title); @@ -197,7 +212,7 @@ export const addFilter = (filter: TimelineFilter): Cypress.Chainable { }; export const createNewTimeline = () => { - cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').click(); - cy.get(TIMELINE_SETTINGS_ICON).should('be.visible'); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(1000); + cy.get(NEW_TIMELINE_ACTION).should('be.visible').trigger('click'); cy.get(CREATE_NEW_TIMELINE).eq(0).should('be.visible').click({ force: true }); }; export const openCreateTimelineOptionsPopover = () => { - cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').click(); + cy.get(NEW_TIMELINE_ACTION).filter(':visible').should('be.visible').click(); }; export const closeCreateTimelineOptionsPopover = () => { @@ -351,7 +363,7 @@ export const expandFirstTimelineEventDetails = () => { * before you're using this task. Otherwise it will fail to save. */ export const saveTimeline = () => { - cy.get(TIMELINE_SAVE_MODAL_OPEN_BUTTON).first().click(); + cy.get(SAVE_TIMELINE_ACTION_BTN).first().click(); cy.get(TIMELINE_SAVE_MODAL).within(() => { cy.get(TIMELINE_PROGRESS_BAR).should('not.exist'); @@ -387,7 +399,6 @@ export const openTimelineInspectButton = () => { }; export const openTimelineFromSettings = () => { - openCreateTimelineOptionsPopover(); cy.get(OPEN_TIMELINE_ICON).should('be.visible'); cy.get(OPEN_TIMELINE_ICON).click(); }; @@ -521,3 +532,9 @@ export const openTimelineFromOpenTimelineModal = (timelineName: string) => { cy.get(OPEN_TIMELINE_MODAL).should('contain.text', timelineName); cy.get(OPEN_TIMELINE_MODAL_TIMELINE_NAMES).first().click(); }; + +export const showDataProviderQueryBuilder = () => { + cy.get(TOGGLE_DATA_PROVIDER_BTN).should('have.attr', 'aria-pressed', 'false'); + cy.get(TOGGLE_DATA_PROVIDER_BTN).trigger('click'); + cy.get(TOGGLE_DATA_PROVIDER_BTN).should('have.attr', 'aria-pressed', 'true'); +}; diff --git a/x-pack/test/security_solution_cypress/package.json b/x-pack/test/security_solution_cypress/package.json index e43f32a447575..e1f552fdba9de 100644 --- a/x-pack/test/security_solution_cypress/package.json +++ b/x-pack/test/security_solution_cypress/package.json @@ -7,9 +7,11 @@ "scripts": { "cypress": "NODE_OPTIONS=--openssl-legacy-provider ../../../node_modules/.bin/cypress", "cypress:open:ess": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ../../test/security_solution_cypress/cypress/cypress.config.ts --ftr-config-file ../../test/security_solution_cypress/cli_config", - "cypress:run:ess": "yarn cypress:ess --spec './cypress/e2e/!(investigations|explore)/**/*.cy.ts'", + "cypress:run:ess": "yarn cypress:ess --spec './cypress/e2e/!(investigations|explore|detection_response/rule_management)/**/*.cy.ts'", "cypress:run:cases:ess": "yarn cypress:ess --spec './cypress/e2e/explore/cases/*.cy.ts'", "cypress:ess": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel run --config-file ../../test/security_solution_cypress/cypress/cypress_ci.config.ts --ftr-config-file ../../test/security_solution_cypress/cli_config", + "cypress:rule_management:run:ess":"yarn cypress:ess --spec './cypress/e2e/detection_response/rule_management/!(prebuilt_rules)/**/*.cy.ts'", + "cypress:rule_management:prebuilt_rules:run:ess": "yarn cypress:ess --spec './cypress/e2e/detection_response/rule_management/prebuilt_rules/**/*.cy.ts'", "cypress:run:respops:ess": "yarn cypress:ess --spec './cypress/e2e/(detection_response|exceptions)/**/*.cy.ts'", "cypress:investigations:run:ess": "yarn cypress:ess --spec './cypress/e2e/investigations/**/*.cy.ts'", "cypress:explore:run:ess": "yarn cypress:ess --spec './cypress/e2e/explore/**/*.cy.ts'", @@ -21,16 +23,20 @@ "cypress:cloud:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider NODE_TLS_REJECT_UNAUTHORIZED=0 ../../../node_modules/.bin/cypress", "cypress:open:cloud:serverless": "yarn cypress:cloud:serverless open --config-file ./cypress/cypress_serverless.config.ts --env CLOUD_SERVERLESS=true", "cypress:open:serverless": "yarn cypress:serverless open --config-file ../../test/security_solution_cypress/cypress/cypress_serverless.config.ts --spec './cypress/e2e/**/*.cy.ts'", - "cypress:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/!(investigations|explore)/**/*.cy.ts'", + "cypress:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/!(investigations|explore|detection_response/rule_management)/**/*.cy.ts'", "cypress:run:cloud:serverless": "yarn cypress:cloud:serverless run --config-file ./cypress/cypress_ci_serverless.config.ts --env CLOUD_SERVERLESS=true", + "cypress:rule_management:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/detection_response/rule_management/!(prebuilt_rules)/**/*.cy.ts'", + "cypress:rule_management:prebuilt_rules:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/detection_response/rule_management/prebuilt_rules/**/*.cy.ts'", "cypress:investigations:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'", "cypress:explore:run:serverless": "yarn cypress:serverless --spec './cypress/e2e/explore/**/*.cy.ts'", "cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=5", "cypress:burn:serverless": "yarn cypress:serverless --env burn=2", "cypress:qa:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel_serverless --config-file ../../test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts", "cypress:open:qa:serverless": "yarn cypress:qa:serverless open", - "cypress:run:qa:serverless": "yarn cypress:qa:serverless --spec './cypress/e2e/!(investigations|explore)/**/*.cy.ts'", + "cypress:run:qa:serverless": "yarn cypress:qa:serverless --spec './cypress/e2e/!(investigations|explore|detection_response/rule_management)/**/*.cy.ts'", "cypress:run:qa:serverless:investigations": "yarn cypress:qa:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'", - "cypress:run:qa:serverless:explore": "yarn cypress:qa:serverless --spec './cypress/e2e/explore/**/*.cy.ts'" + "cypress:run:qa:serverless:explore": "yarn cypress:qa:serverless --spec './cypress/e2e/explore/**/*.cy.ts'", + "cypress:run:qa:serverless:rule_management": "yarn cypress:qa:serverless --spec './cypress/e2e/detection_response/rule_management/!(prebuilt_rules)/**/*.cy.ts'", + "cypress:run:qa:serverless:rule_management:prebuilt_rules": "yarn cypress:qa:serverless --spec './cypress/e2e/detection_response/rule_management/prebuilt_rules/**/*.cy.ts'" } } \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/serverless_config.ts b/x-pack/test/security_solution_cypress/serverless_config.ts index f302617f0e740..d0ee1613f6e4c 100644 --- a/x-pack/test/security_solution_cypress/serverless_config.ts +++ b/x-pack/test/security_solution_cypress/serverless_config.ts @@ -34,9 +34,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { { product_line: 'endpoint', product_tier: 'complete' }, { product_line: 'cloud', product_tier: 'complete' }, ])}`, - `--xpack.securitySolution.enableExperimental=${JSON.stringify([ - 'disableTimelineSaveTour', - ])}`, ], }, testRunner: SecuritySolutionConfigurableCypressTestRunner, diff --git a/x-pack/test/security_solution_endpoint/config.base.ts b/x-pack/test/security_solution_endpoint/config.base.ts index d75458a4c581d..e3be9a2a6934b 100644 --- a/x-pack/test/security_solution_endpoint/config.base.ts +++ b/x-pack/test/security_solution_endpoint/config.base.ts @@ -90,10 +90,6 @@ export const generateConfig = async ({ `--xpack.fleet.packages.0.version=latest`, // this will be removed in 8.7 when the file upload feature is released `--xpack.fleet.enableExperimental.0=diagnosticFileUploadEnabled`, - // disable a tour that prevents tests from passing - `--xpack.securitySolution.enableExperimental=${JSON.stringify([ - 'disableTimelineSaveTour', - ])}`, ...kbnServerArgs, ], }, diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts index 8d297f82befee..f904539dae231 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/execute.ts @@ -16,9 +16,7 @@ export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const endpointTestResources = getService('endpointTestResources'); - // FLAKY: https://github.com/elastic/kibana/issues/171667 - // FLAKY: https://github.com/elastic/kibana/issues/171666 - describe.skip('Endpoint `execute` response action', function () { + describe('Endpoint `execute` response action', function () { targetTags(this, ['@ess', '@serverless']); let indexedData: IndexedHostsAndAlertsResponse; diff --git a/x-pack/test/security_solution_ftr/page_objects/timeline/index.ts b/x-pack/test/security_solution_ftr/page_objects/timeline/index.ts index b6c41a813b07e..e7f35e31702cc 100644 --- a/x-pack/test/security_solution_ftr/page_objects/timeline/index.ts +++ b/x-pack/test/security_solution_ftr/page_objects/timeline/index.ts @@ -15,11 +15,9 @@ const TIMELINE_MODAL_PAGE_TEST_SUBJ = 'timeline'; const TIMELINE_TAB_QUERY_TEST_SUBJ = 'timeline-tab-content-query'; const TIMELINE_CSS_SELECTOR = Object.freeze({ - /** The Plus icon to add a new timeline located in the bottom timeline sticky bar */ - buttonBarAddButton: `${testSubjSelector( + bottomBarTimelineTitle: `${testSubjSelector( TIMELINE_BOTTOM_BAR_CONTAINER_TEST_SUBJ - )} ${testSubjSelector('settings-plus-in-circle')}`, - + )} ${testSubjSelector('timeline-title')}`, /** The refresh button on the timeline view (top of view, next to the date selector) */ refreshButton: `${testSubjSelector(TIMELINE_TAB_QUERY_TEST_SUBJ)} ${testSubjSelector( 'superDatePickerApplyTimeButton' @@ -45,16 +43,15 @@ export class TimelinePageObject extends FtrService { await this.testSubjects.existOrFail(TIMELINE_BOTTOM_BAR_CONTAINER_TEST_SUBJ); } - async showOpenTimelinePopupFromBottomBar(): Promise { + async openTimelineFromBottomBar() { await this.ensureTimelineAccessible(); await this.testSubjects.findService.clickByCssSelector( - TIMELINE_CSS_SELECTOR.buttonBarAddButton + TIMELINE_CSS_SELECTOR.bottomBarTimelineTitle ); - await this.testSubjects.existOrFail('timeline-addPopupPanel'); } async openTimelineById(id: string): Promise { - await this.showOpenTimelinePopupFromBottomBar(); + await this.openTimelineFromBottomBar(); await this.testSubjects.click('open-timeline-button'); await this.testSubjects.findService.clickByCssSelector( `${testSubjSelector('open-timeline-modal')} ${testSubjSelector(`timeline-title-${id}`)}` diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 025dff494c5fe..0c763cbf20c5e 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -67,7 +67,6 @@ "@kbn/ftr-common-functional-services", "@kbn/securitysolution-io-ts-list-types", "@kbn/securitysolution-list-constants", - "@kbn/securitysolution-es-utils", "@kbn/expect", "@kbn/dev-cli-errors", "@kbn/ci-stats-reporter", diff --git a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts index 105620f90d6b7..2d54d3ba0a52b 100644 --- a/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/dashboard/dashboard_smoke_tests.ts @@ -14,6 +14,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const log = getService('log'); const renderable = getService('renderable'); const dashboardExpect = getService('dashboardExpect'); + const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'timePicker']); const browser = getService('browser'); @@ -40,8 +41,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); dashboardTests.forEach(({ name, numPanels }) => { it('should launch sample ' + name + ' data set dashboard', async () => { + await kibanaServer.uiSettings.update({ + 'timepicker:timeDefaults': `{ "from": "now-5y", "to": "now"}`, + }); await PageObjects.home.launchSampleDashboard(name); - await PageObjects.timePicker.setCommonlyUsedTime('Last_1 year'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); const panelCount = await PageObjects.dashboard.getPanelCount(); @@ -49,9 +52,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); it('should render visualizations', async () => { + await kibanaServer.uiSettings.update({ + 'timepicker:timeDefaults': `{ "from": "now-5y", "to": "now"}`, + }); await PageObjects.home.launchSampleDashboard('flights'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.timePicker.setCommonlyUsedTime('Last_1 year'); await renderable.waitForRender(); log.debug('Checking saved searches rendered'); await dashboardExpect.savedSearchRowCount(49); diff --git a/x-pack/test_serverless/api_integration/services/saml_tools.ts b/x-pack/test_serverless/api_integration/services/saml_tools.ts index 4756109fc667d..bd5cd03a7edbb 100644 --- a/x-pack/test_serverless/api_integration/services/saml_tools.ts +++ b/x-pack/test_serverless/api_integration/services/saml_tools.ts @@ -6,33 +6,35 @@ */ import expect from '@kbn/expect'; -// eslint-disable-next-line @kbn/imports/no_boundary_crossing -import { getSAMLResponse } from '@kbn/security-api-integration-helpers/saml/saml_tools'; -import { kbnTestConfig } from '@kbn/test'; - import { parse as parseCookie } from 'tough-cookie'; +import Url from 'url'; +import { createSAMLResponse } from '@kbn/mock-idp-plugin/common'; import { FtrProviderContext } from '../ftr_provider_context'; export function SamlToolsProvider({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const randomness = getService('randomness'); const svlCommonApi = getService('svlCommonApi'); - - function createSAMLResponse(options = {}) { - return getSAMLResponse({ - destination: `http://localhost:${kbnTestConfig.getPort()}/api/security/saml/callback`, - sessionIndex: String(randomness.naturalNumber()), - ...options, - }); - } + const config = getService('config'); return { async login(username: string) { + const kibanaUrl = Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + pathname: '/api/security/saml/callback', + }); const samlAuthenticationResponse = await supertestWithoutAuth .post('/api/security/saml/callback') .set(svlCommonApi.getCommonRequestHeader()) - .send({ SAMLResponse: await createSAMLResponse({ username }) }); + .send({ + SAMLResponse: await createSAMLResponse({ + username, + roles: [], + kibanaUrl, + }), + }); expect(samlAuthenticationResponse.status).to.equal(302); expect(samlAuthenticationResponse.header.location).to.equal('/'); const sessionCookie = parseCookie(samlAuthenticationResponse.header['set-cookie'][0])!; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/core/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/core/index.ts index aa847e0c0ecea..d48a40baa22f6 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/core/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/core/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./compression')); loadTestFile(require.resolve('./translations')); loadTestFile(require.resolve('./capabilities')); + loadTestFile(require.resolve('./ui_settings')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/core/ui_settings.ts b/x-pack/test_serverless/api_integration/test_suites/common/core/ui_settings.ts new file mode 100644 index 0000000000000..028b0ab69b438 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/core/ui_settings.ts @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +// To test setting validations we are using the existing 'defaultColumns' setting that is available in all serverless projects +// (See list of common serverless settings in /packages/serverless/settings/common/index.ts) +// The 'defaultColumns' setting is of type array of strings +const DEFAULT_COLUMNS_SETTING = 'defaultColumns'; + +// We will also create a test setting +const TEST_SETTING = 'testSetting'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + describe('ui settings service', () => { + before(async () => { + // Creating a test setting + await supertest + .post(`/internal/kibana/settings/${TEST_SETTING}`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: 100 }) + .expect(200); + }); + + // We don't test the public routes as they are not available in serverless + describe('internal routes', () => { + describe('get', () => { + it('returns list of settings', async () => { + const { body } = await supertest + .get('/internal/kibana/settings') + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200); + + // The returned list of settings should contain the created test setting + expect(body).to.have.property('settings'); + expect(body.settings).to.have.property(TEST_SETTING); + }); + }); + + describe('set', () => { + it('validates value', async () => { + const { body } = await supertest + .post(`/internal/kibana/settings/${DEFAULT_COLUMNS_SETTING}`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: 100 }) + .expect(400); + + expect(body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + '[validation [defaultColumns]]: expected value of type [array] but got [number]', + }); + }); + + it('sets value of a setting', async () => { + await supertest + .post(`/internal/kibana/settings/${TEST_SETTING}`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: 999 }) + .expect(200); + + // Verify that the setting has a new value + const { body } = await supertest + .get('/internal/kibana/settings') + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200); + + // The returned list of settings should contain the created test setting + expect(body.settings[TEST_SETTING].userValue).to.equal(999); + }); + }); + + describe('set many', () => { + it('validates value', async () => { + const { body } = await supertest + .post('/internal/kibana/settings') + .set(svlCommonApi.getInternalRequestHeader()) + .send({ changes: { [TEST_SETTING]: 100, [DEFAULT_COLUMNS_SETTING]: 100 } }) + .expect(400); + + expect(body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + '[validation [defaultColumns]]: expected value of type [array] but got [number]', + }); + }); + + it('sets values of settings', async () => { + await supertest + .post(`/internal/kibana/settings`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ changes: { [TEST_SETTING]: 500 } }) + .expect(200); + + // Verify that the setting has a new value + const { body } = await supertest + .get('/internal/kibana/settings') + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200); + + // The returned list of settings should contain the created test setting + expect(body.settings[TEST_SETTING].userValue).to.equal(500); + }); + }); + + describe('validate', () => { + it('returns correct validation error message for invalid value', async () => { + const { body } = await supertest + .post(`/internal/kibana/settings/${DEFAULT_COLUMNS_SETTING}/validate`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: 100 }) + .expect(200); + + expect(body).to.eql({ + valid: false, + errorMessage: 'expected value of type [array] but got [number]', + }); + }); + + it('returns no validation error message for valid value', async () => { + const { body } = await supertest + .post(`/internal/kibana/settings/${DEFAULT_COLUMNS_SETTING}/validate`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: ['test'] }) + .expect(200); + + expect(body).to.eql({ + valid: true, + }); + }); + + it('returns a 404 for non-existing key', async () => { + const { body } = await supertest + .post(`/internal/kibana/settings/nonExisting/validate`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: ['test'] }) + .expect(404); + + expect(body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Setting with a key [nonExisting] does not exist.', + }); + }); + + it('returns a 400 for a null value', async () => { + const { body } = await supertest + .post(`/internal/kibana/settings/${DEFAULT_COLUMNS_SETTING}/validate`) + .set(svlCommonApi.getInternalRequestHeader()) + .send({ value: null }) + .expect(400); + + expect(body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'No value was specified.', + }); + }); + }); + + describe('delete', () => { + it('deletes setting', async () => { + await supertest + .delete(`/internal/kibana/settings/${TEST_SETTING}`) + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200); + + // Verify that the setting is not returned in the Get response anymore + const { body } = await supertest + .get('/internal/kibana/settings') + .set(svlCommonApi.getInternalRequestHeader()) + .expect(200); + + // The returned list of settings should contain the created test setting + expect(body.settings).to.not.have.property(TEST_SETTING); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/cluster_nodes.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/cluster_nodes.ts new file mode 100644 index 0000000000000..60110dd5af3fc --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/cluster_nodes.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const indexManagementService = getService('indexManagement'); + + describe('nodes', () => { + let getNodesPlugins: typeof indexManagementService['clusterNodes']['api']['getNodesPlugins']; + + before(async () => { + ({ + clusterNodes: { + api: { getNodesPlugins }, + }, + } = indexManagementService); + }); + + it('should fetch the nodes plugins', async () => { + const { body } = await getNodesPlugins().expect(200); + + expect(Array.isArray(body)).to.be(true); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts new file mode 100644 index 0000000000000..adc84ffecb638 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts @@ -0,0 +1,325 @@ +/* + * Copyright 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 { DataStream } from '@kbn/index-management-plugin/common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const API_BASE_PATH = '/api/index_management'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const indexManagementService = getService('indexManagement'); + let helpers: typeof indexManagementService['datastreams']['helpers']; + let createDataStream: typeof helpers['createDataStream']; + let deleteDataStream: typeof helpers['deleteDataStream']; + let deleteComposableIndexTemplate: typeof helpers['deleteComposableIndexTemplate']; + let updateIndexTemplateMappings: typeof helpers['updateIndexTemplateMappings']; + let getMapping: typeof helpers['getMapping']; + let getDatastream: typeof helpers['getDatastream']; + + describe('Data streams', function () { + before(async () => { + ({ + datastreams: { helpers }, + } = indexManagementService); + ({ + createDataStream, + deleteDataStream, + deleteComposableIndexTemplate, + updateIndexTemplateMappings, + getMapping, + getDatastream, + } = helpers); + }); + describe('Get', () => { + const testDataStreamName = 'test-data-stream'; + + before(async () => await createDataStream(testDataStreamName)); + after(async () => await deleteDataStream(testDataStreamName)); + + it('returns an array of data streams', async () => { + const { body: dataStreams } = await supertest + .get(`${API_BASE_PATH}/data_streams`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + expect(dataStreams).to.be.an('array'); + + // returned array can contain automatically created data streams + const testDataStream = dataStreams.find( + (dataStream: DataStream) => dataStream.name === testDataStreamName + ); + + expect(testDataStream).to.be.ok(); + + // ES determines these values so we'll just echo them back. + const { name: indexName, uuid } = testDataStream!.indices[0]; + + expect(testDataStream).to.eql({ + name: testDataStreamName, + lifecycle: { + enabled: true, + }, + privileges: { + delete_index: true, + manage_data_stream_lifecycle: true, + }, + timeStampField: { name: '@timestamp' }, + indices: [ + { + name: indexName, + uuid, + preferILM: true, + managedBy: 'Data stream lifecycle', + }, + ], + nextGenerationManagedBy: 'Data stream lifecycle', + generation: 1, + health: 'green', + indexTemplateName: testDataStreamName, + hidden: false, + }); + }); + + it('includes stats when provided the includeStats query parameter', async () => { + const { body: dataStreams } = await supertest + .get(`${API_BASE_PATH}/data_streams?includeStats=true`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + expect(dataStreams).to.be.an('array'); + + // returned array can contain automatically created data streams + const testDataStream = dataStreams.find( + (dataStream: DataStream) => dataStream.name === testDataStreamName + ); + + expect(testDataStream).to.be.ok(); + + // ES determines these values so we'll just echo them back. + const { name: indexName, uuid } = testDataStream!.indices[0]; + const { storageSize, storageSizeBytes, ...dataStreamWithoutStorageSize } = testDataStream!; + + expect(dataStreamWithoutStorageSize).to.eql({ + name: testDataStreamName, + privileges: { + delete_index: true, + manage_data_stream_lifecycle: true, + }, + timeStampField: { name: '@timestamp' }, + indices: [ + { + name: indexName, + managedBy: 'Data stream lifecycle', + preferILM: true, + uuid, + }, + ], + generation: 1, + health: 'green', + indexTemplateName: testDataStreamName, + nextGenerationManagedBy: 'Data stream lifecycle', + maxTimeStamp: 0, + hidden: false, + lifecycle: { + enabled: true, + }, + }); + }); + + it('returns a single data stream by ID', async () => { + const { body: dataStream } = await supertest + .get(`${API_BASE_PATH}/data_streams/${testDataStreamName}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + // ES determines these values so we'll just echo them back. + const { name: indexName, uuid } = dataStream.indices[0]; + const { storageSize, storageSizeBytes, ...dataStreamWithoutStorageSize } = dataStream; + + expect(dataStreamWithoutStorageSize).to.eql({ + name: testDataStreamName, + privileges: { + delete_index: true, + manage_data_stream_lifecycle: true, + }, + timeStampField: { name: '@timestamp' }, + indices: [ + { + name: indexName, + managedBy: 'Data stream lifecycle', + preferILM: true, + uuid, + }, + ], + generation: 1, + health: 'green', + indexTemplateName: testDataStreamName, + nextGenerationManagedBy: 'Data stream lifecycle', + maxTimeStamp: 0, + hidden: false, + lifecycle: { + enabled: true, + }, + }); + }); + }); + + describe('Update', () => { + const testDataStreamName = 'test-data-stream'; + + before(async () => await createDataStream(testDataStreamName)); + after(async () => await deleteDataStream(testDataStreamName)); + + it('updates the data retention of a DS', async () => { + const { body } = await supertest + .put(`${API_BASE_PATH}/data_streams/${testDataStreamName}/data_retention`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({ + dataRetention: '7d', + }) + .expect(200); + + expect(body).to.eql({ success: true }); + }); + + it('sets data retention to infinite', async () => { + const { body } = await supertest + .put(`${API_BASE_PATH}/data_streams/${testDataStreamName}/data_retention`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({}) + .expect(200); + + expect(body).to.eql({ success: true }); + }); + + it('can disable lifecycle for a given policy', async () => { + const { body } = await supertest + .put(`${API_BASE_PATH}/data_streams/${testDataStreamName}/data_retention`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({ enabled: false }) + .expect(200); + + expect(body).to.eql({ success: true }); + + const datastream = await getDatastream(testDataStreamName); + expect(datastream.lifecycle).to.be(undefined); + }); + }); + + describe('Delete', () => { + const testDataStreamName1 = 'test-data-stream1'; + const testDataStreamName2 = 'test-data-stream2'; + + before(async () => { + await Promise.all([ + createDataStream(testDataStreamName1), + createDataStream(testDataStreamName2), + ]); + }); + + after(async () => { + // The Delete API only deletes the data streams, so we still need to manually delete their + // related index patterns to clean up. + await Promise.all([ + deleteComposableIndexTemplate(testDataStreamName1), + deleteComposableIndexTemplate(testDataStreamName2), + ]); + }); + + it('deletes multiple data streams', async () => { + await supertest + .post(`${API_BASE_PATH}/delete_data_streams`) + .set('x-elastic-internal-origin', 'xxx') + .set('kbn-xsrf', 'xxx') + .send({ + dataStreams: [testDataStreamName1, testDataStreamName2], + }) + .expect(200); + + await supertest + .get(`${API_BASE_PATH}/data_streams/${testDataStreamName1}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(404); + + await supertest + .get(`${API_BASE_PATH}/data_streams/${testDataStreamName2}`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(404); + }); + }); + + describe('Mappings from template', () => { + const testDataStreamName1 = 'test-data-stream-mappings-1'; + + before(async () => { + await createDataStream(testDataStreamName1); + }); + + after(async () => { + await deleteDataStream(testDataStreamName1); + }); + + it('Apply mapping from index template', async () => { + const beforeMapping = await getMapping(testDataStreamName1); + expect(beforeMapping.properties).eql({ + '@timestamp': { type: 'date' }, + }); + await updateIndexTemplateMappings(testDataStreamName1, { + properties: { + test: { type: 'integer' }, + }, + }); + await supertest + .post(`${API_BASE_PATH}/data_streams/${testDataStreamName1}/mappings_from_template`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + const afterMapping = await getMapping(testDataStreamName1); + expect(afterMapping.properties).eql({ + '@timestamp': { type: 'date' }, + test: { type: 'integer' }, + }); + }); + }); + + describe('Rollover', () => { + const testDataStreamName1 = 'test-data-stream-rollover-1'; + + before(async () => { + await createDataStream(testDataStreamName1); + }); + + after(async () => { + await deleteDataStream(testDataStreamName1); + }); + + it('Rollover datastreams', async () => { + await supertest + .post(`${API_BASE_PATH}/data_streams/${testDataStreamName1}/rollover`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + const datastream = await getDatastream(testDataStreamName1); + + expect(datastream.generation).equal(2); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts index 7dff563bf43b3..abed984a86245 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts @@ -14,5 +14,10 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_templates')); loadTestFile(require.resolve('./indices')); loadTestFile(require.resolve('./create_enrich_policies')); + loadTestFile(require.resolve('./index_component_templates')); + loadTestFile(require.resolve('./cluster_nodes')); + loadTestFile(require.resolve('./datastreams')); + loadTestFile(require.resolve('./mappings')); + loadTestFile(require.resolve('./settings')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_component_templates.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_component_templates.ts new file mode 100644 index 0000000000000..9781a34fe0803 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_component_templates.ts @@ -0,0 +1,437 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const CACHE_TEMPLATES = true; + +export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); + const indexManagementService = getService('indexManagement'); + + describe('component templates', () => { + // Api methods + let getAllComponentTemplates: typeof indexManagementService['componentTemplates']['api']['getAllComponentTemplates']; + let getOneComponentTemplate: typeof indexManagementService['componentTemplates']['api']['getOneComponentTemplate']; + let updateComponentTemplate: typeof indexManagementService['componentTemplates']['api']['updateComponentTemplate']; + let deleteComponentTemplate: typeof indexManagementService['componentTemplates']['api']['deleteComponentTemplate']; + let getComponentTemplateDatastreams: typeof indexManagementService['componentTemplates']['api']['getComponentTemplateDatastreams']; + // Helpers + let addDatastream: typeof indexManagementService['componentTemplates']['helpers']['addDatastream']; + let addIndexTemplate: typeof indexManagementService['componentTemplates']['helpers']['addIndexTemplate']; + let addComponentTemplate: typeof indexManagementService['componentTemplates']['helpers']['addComponentTemplate']; + let cleanupDatastreams: typeof indexManagementService['componentTemplates']['helpers']['cleanupDatastreams']; + let cleanUpIndexTemplates: typeof indexManagementService['componentTemplates']['helpers']['cleanUpIndexTemplates']; + let cleanUpComponentTemplates: typeof indexManagementService['componentTemplates']['helpers']['cleanUpComponentTemplates']; + + before(async () => { + ({ + componentTemplates: { + api: { + getAllComponentTemplates, + getOneComponentTemplate, + updateComponentTemplate, + deleteComponentTemplate, + getComponentTemplateDatastreams, + }, + helpers: { + addDatastream, + addIndexTemplate, + addComponentTemplate, + cleanupDatastreams, + cleanUpIndexTemplates, + cleanUpComponentTemplates, + }, + }, + } = indexManagementService); + }); + + after(async () => { + await cleanUpIndexTemplates(); + await cleanUpComponentTemplates(); + await cleanupDatastreams(); + }); + + describe('Get', () => { + const COMPONENT_NAME = 'test_get_component_template'; + const COMPONENT = { + template: { + settings: { + index: { + number_of_shards: 1, + }, + }, + mappings: { + _source: { + enabled: false, + }, + properties: { + host_name: { + type: 'keyword', + }, + created_at: { + type: 'date', + format: 'EEE MMM dd HH:mm:ss Z yyyy', + }, + }, + }, + }, + }; + + // Create component template to verify GET requests + before(async () => { + try { + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); + } catch (err) { + log.debug('[Setup error] Error creating component template'); + throw err; + } + }); + + describe('all component templates', () => { + it('should return an array of component templates', async () => { + const { body: componentTemplates } = await getAllComponentTemplates().expect(200); + + const testComponentTemplate = componentTemplates.find( + ({ name }: { name: string }) => name === COMPONENT_NAME + ); + + expect(testComponentTemplate).to.eql({ + name: COMPONENT_NAME, + usedBy: [], + isManaged: false, + hasSettings: true, + hasMappings: true, + hasAliases: false, + }); + }); + }); + + describe('one component template', () => { + it('should return a single component template', async () => { + const { body } = await getOneComponentTemplate(COMPONENT_NAME).expect(200); + + expect(body).to.eql({ + name: COMPONENT_NAME, + ...COMPONENT, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }); + }); + }); + }); + + describe('Update', () => { + const COMPONENT_NAME = 'test_update_component_template'; + const COMPONENT = { + template: { + settings: { + index: { + number_of_shards: 1, + }, + }, + mappings: { + _source: { + enabled: false, + }, + properties: { + host_name: { + type: 'keyword', + }, + created_at: { + type: 'date', + format: 'EEE MMM dd HH:mm:ss Z yyyy', + }, + }, + }, + }, + }; + + before(async () => { + // Create component template that can be used to test PUT request + try { + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); + } catch (err) { + log.debug('[Setup error] Error creating component template'); + throw err; + } + }); + + it('should allow an existing component template to be updated', async () => { + const { body } = await updateComponentTemplate(COMPONENT_NAME, { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + }); + + it('should not allow a non-existing component template to be updated', async () => { + const { body } = await updateComponentTemplate('component_does_not_exist', { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(404); + + expect(body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'component template matching [component_does_not_exist] not found', + attributes: { + error: { + reason: 'component template matching [component_does_not_exist] not found', + root_cause: [ + { + reason: 'component template matching [component_does_not_exist] not found', + type: 'resource_not_found_exception', + }, + ], + type: 'resource_not_found_exception', + }, + }, + }); + }); + }); + + describe('Update', () => { + const COMPONENT_NAME = 'test_update_component_template'; + const COMPONENT = { + template: { + settings: { + index: { + number_of_shards: 1, + }, + }, + mappings: { + _source: { + enabled: false, + }, + properties: { + host_name: { + type: 'keyword', + }, + created_at: { + type: 'date', + format: 'EEE MMM dd HH:mm:ss Z yyyy', + }, + }, + }, + }, + }; + + before(async () => { + // Create component template that can be used to test PUT request + try { + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); + } catch (err) { + log.debug('[Setup error] Error creating component template'); + throw err; + } + }); + + it('should allow an existing component template to be updated', async () => { + const { body } = await updateComponentTemplate(COMPONENT_NAME, { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + }); + + it('should not allow a non-existing component template to be updated', async () => { + const { body } = await updateComponentTemplate('component_does_not_exist', { + ...COMPONENT, + version: 1, + _kbnMeta: { + usedBy: [], + isManaged: false, + }, + }).expect(404); + + expect(body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'component template matching [component_does_not_exist] not found', + attributes: { + error: { + reason: 'component template matching [component_does_not_exist] not found', + root_cause: [ + { + reason: 'component template matching [component_does_not_exist] not found', + type: 'resource_not_found_exception', + }, + ], + type: 'resource_not_found_exception', + }, + }, + }); + }); + }); + + describe('Delete', () => { + const COMPONENT = { + template: { + settings: { + index: { + number_of_shards: 1, + }, + }, + }, + }; + + const componentTemplateA = { body: COMPONENT, name: 'test_delete_component_template_a' }; + const componentTemplateB = { body: COMPONENT, name: 'test_delete_component_template_b' }; + const componentTemplateC = { body: COMPONENT, name: 'test_delete_component_template_c' }; + const componentTemplateD = { body: COMPONENT, name: 'test_delete_component_template_d' }; + + before(async () => { + // Create several component templates that can be used to test deletion + await Promise.all( + [componentTemplateA, componentTemplateB, componentTemplateC, componentTemplateD].map( + (template) => addComponentTemplate(template, !CACHE_TEMPLATES) + ) + ).catch((err) => { + log.debug(`[Setup error] Error creating component templates: ${err.message}`); + throw err; + }); + }); + + it('should delete a component template', async () => { + const { name } = componentTemplateA; + const { body } = await deleteComponentTemplate(name).expect(200); + + expect(body).to.eql({ + itemsDeleted: [name], + errors: [], + }); + }); + + it('should delete multiple component templates', async () => { + const { name: componentTemplate1Name } = componentTemplateB; + const { name: componentTemplate2Name } = componentTemplateC; + + const { + body: { itemsDeleted, errors }, + } = await deleteComponentTemplate( + `${componentTemplate1Name},${componentTemplate2Name}` + ).expect(200); + + expect(errors).to.eql([]); + + // The itemsDeleted array order isn't guaranteed, so we assert against each name instead + [componentTemplate1Name, componentTemplate2Name].forEach((componentName) => { + expect(itemsDeleted.includes(componentName)).to.be(true); + }); + }); + + it('should return an error for any component templates not sucessfully deleted', async () => { + const COMPONENT_DOES_NOT_EXIST = 'component_does_not_exist'; + const { name: componentTemplateName } = componentTemplateD; + + const { body } = await deleteComponentTemplate( + `${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}` + ).expect(200); + expect(body.itemsDeleted).to.eql([componentTemplateName]); + expect(body.errors[0].name).to.eql(COMPONENT_DOES_NOT_EXIST); + + expect(body.errors[0].error.payload.attributes.error).to.eql({ + root_cause: [ + { + type: 'resource_not_found_exception', + reason: 'component_does_not_exist', + }, + ], + type: 'resource_not_found_exception', + reason: 'component_does_not_exist', + }); + }); + }); + + describe('Get datastreams', () => { + const COMPONENT_NAME = 'test_get_component_template_datastreams'; + const COMPONENT = { + template: { + settings: { + index: { + number_of_shards: 1, + }, + }, + mappings: { + _source: { + enabled: false, + }, + properties: { + host_name: { + type: 'keyword', + }, + created_at: { + type: 'date', + format: 'EEE MMM dd HH:mm:ss Z yyyy', + }, + }, + }, + }, + }; + const DATASTREAM_NAME = 'logs-test-component-template-default'; + const INDEX_PATTERN = 'logs-test-component-template-*'; + const TEMPLATE_NAME = 'test_get_component_template_datastreams'; + const TEMPLATE = { + index_patterns: INDEX_PATTERN, + composed_of: [COMPONENT_NAME], + }; + + // Create component template to verify GET requests + before(async () => { + try { + await addComponentTemplate({ body: COMPONENT, name: COMPONENT_NAME }, CACHE_TEMPLATES); + await addIndexTemplate({ body: TEMPLATE, name: TEMPLATE_NAME }, CACHE_TEMPLATES); + } catch (err) { + log.debug('[Setup error] Error creating component template'); + throw err; + } + }); + + describe('without datastreams', () => { + it('should return no datastreams', async () => { + const { body } = await getComponentTemplateDatastreams(COMPONENT_NAME).expect(200); + + expect(body).to.eql({ data_streams: [] }); + }); + }); + + describe('with datastreams', () => { + before(async () => { + await addDatastream(DATASTREAM_NAME, CACHE_TEMPLATES); + }); + it('should return datastreams', async () => { + const { body } = await getComponentTemplateDatastreams(COMPONENT_NAME).expect(200); + + expect(body).to.eql({ data_streams: ['logs-test-component-template-default'] }); + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/indices.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/indices.ts index 9c4fd7b196337..70de83c949cf5 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/indices.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/indices.ts @@ -5,10 +5,9 @@ * 2.0. */ -import expect from 'expect'; +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -const API_BASE_PATH = '/api/index_management'; const INTERNAL_API_BASE_PATH = '/internal/index_management'; const expectedKeys = ['aliases', 'hidden', 'isFrozen', 'primary', 'replica', 'name'].sort(); @@ -16,22 +15,27 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const es = getService('es'); const log = getService('log'); + const indexManagementService = getService('indexManagement'); describe('Indices', function () { - const indexName = `index-${Math.random()}`; + let indexName: string; + let reload: typeof indexManagementService['indices']['api']['reload']; + let list: typeof indexManagementService['indices']['api']['list']; + let deleteIndex: typeof indexManagementService['indices']['api']['deleteIndex']; + let createIndex: typeof indexManagementService['indices']['helpers']['createIndex']; + let deleteAllIndices: typeof indexManagementService['indices']['helpers']['deleteAllIndices']; + let catIndex: typeof indexManagementService['indices']['helpers']['catIndex']; before(async () => { - // Create a new index to test against - const indexExists = await es.indices.exists({ index: indexName }); - - // Index should not exist, but in the case that it already does, we bypass the create request - if (indexExists) { - return; - } - + ({ + indices: { + api: { reload, list, deleteIndex }, + helpers: { createIndex, deleteAllIndices, catIndex }, + }, + } = indexManagementService); log.debug(`Creating index: '${indexName}'`); try { - await es.indices.create({ index: indexName }); + indexName = await createIndex(); } catch (err) { log.debug('[Setup error] Error creating index'); throw err; @@ -41,28 +45,27 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { // Cleanup index created for testing purposes try { - await es.indices.delete({ - index: indexName, - }); + await deleteAllIndices(); } catch (err) { log.debug('[Cleanup error] Error deleting index'); throw err; } }); - describe('get all', () => { - it('should list indices with the expected parameters', async () => { - const { body: indices } = await supertest - .get(`${API_BASE_PATH}/indices`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') - .expect(200); + describe('list', () => { + it('should list all the indices with the expected properties', async function () { + // Create an index that we can assert against + await createIndex('test_index'); - const indexFound = indices.find((index: { name: string }) => index.name === indexName); + // Verify indices request + const { body: indices } = await list().set('x-elastic-internal-origin', 'xxx').expect(200); - expect(indexFound).toBeTruthy(); + // Find the "test_index" created to verify expected keys + const indexCreated = indices.find((index: { name: string }) => index.name === 'test_index'); - expect(Object.keys(indexFound).sort()).toEqual(expectedKeys); + const sortedReceivedKeys = Object.keys(indexCreated).sort(); + + expect(sortedReceivedKeys).to.eql(expectedKeys); }); }); @@ -74,9 +77,9 @@ export default function ({ getService }: FtrProviderContext) { .set('x-elastic-internal-origin', 'xxx') .expect(200); - expect(index).toBeTruthy(); + expect(index).to.be.ok(); - expect(Object.keys(index).sort()).toEqual(expectedKeys); + expect(Object.keys(index).sort()).to.eql(expectedKeys); }); it('throws 404 for a non-existent index', async () => { @@ -118,9 +121,9 @@ export default function ({ getService }: FtrProviderContext) { .set('x-elastic-internal-origin', 'xxx') .expect(200); - expect(index).toBeTruthy(); + expect(index).to.be.ok(); - expect(Object.keys(index).sort()).toEqual(expectedKeys); + expect(Object.keys(index).sort()).to.eql(expectedKeys); }); it('fails to re-create the same index', async () => { @@ -134,5 +137,47 @@ export default function ({ getService }: FtrProviderContext) { .expect(400); }); }); + + describe('reload', function () { + it('should list all the indices with the expected properties', async function () { + // create an index to assert against, otherwise the test is flaky + await createIndex('reload-test-index'); + const { body } = await reload().set('x-elastic-internal-origin', 'xxx').expect(200); + + const indexCreated = body.find( + (index: { name: string }) => index.name === 'reload-test-index' + ); + const sortedReceivedKeys = Object.keys(indexCreated).sort(); + expect(sortedReceivedKeys).to.eql(expectedKeys); + expect(body.length > 1).to.be(true); // to contrast it with the next test + }); + + it('should allow reloading only certain indices', async () => { + const index = await createIndex(); + const { body } = await reload([index]).set('x-elastic-internal-origin', 'xxx'); + + expect(body.length === 1).to.be(true); + expect(body[0].name).to.be(index); + }); + }); + + describe('delete indices', () => { + it('should delete an index', async () => { + const index = await createIndex(); + + const { body: indices1 } = await catIndex(undefined, 'i'); + expect(indices1.map((indexItem) => indexItem.i)).to.contain(index); + + await deleteIndex(index).set('x-elastic-internal-origin', 'xxx').expect(200); + + const { body: indices2 } = await catIndex(undefined, 'i'); + expect(indices2.map((indexItem) => indexItem.i)).not.to.contain(index); + }); + + it('should require index or indices to be provided', async () => { + const { body } = await deleteIndex().set('x-elastic-internal-origin', 'xxx').expect(400); + expect(body.message).to.contain('expected value of type [string]'); + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/mappings.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/mappings.ts new file mode 100644 index 0000000000000..eb28c77fc4409 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/mappings.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const log = getService('log'); + const indexManagementService = getService('indexManagement'); + + describe('mappings', () => { + let indexName: string; + let getMapping: typeof indexManagementService['mappings']['api']['getMapping']; + let createIndex: typeof indexManagementService['indices']['helpers']['createIndex']; + let deleteAllIndices: typeof indexManagementService['indices']['helpers']['deleteAllIndices']; + + const mappings = { + properties: { + total: { type: 'long' }, + tag: { type: 'keyword' }, + createdAt: { type: 'date' }, + }, + }; + + before(async () => { + ({ + indices: { + helpers: { createIndex, deleteAllIndices }, + }, + mappings: { + api: { getMapping }, + }, + } = indexManagementService); + + log.debug('Creating index'); + try { + indexName = await createIndex(undefined, mappings); + } catch (err) { + log.debug('[Setup error] Error creating index'); + throw err; + } + }); + + after(async () => { + try { + await deleteAllIndices(); + } catch (err) { + log.debug('[Cleanup error] Error deleting index'); + throw err; + } + }); + + it('should get the index mappings', async () => { + const { body } = await getMapping(indexName).expect(200); + + expect(body.mappings).to.eql(mappings); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/settings.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/settings.ts new file mode 100644 index 0000000000000..d5cd93060737c --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/settings.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const indexManagementService = getService('indexManagement'); + + describe('settings', () => { + let getIndexSettings: typeof indexManagementService['settings']['api']['getIndexSettings']; + let updateIndexSettings: typeof indexManagementService['settings']['api']['updateIndexSettings']; + let createIndex: typeof indexManagementService['indices']['helpers']['createIndex']; + let deleteAllIndices: typeof indexManagementService['indices']['helpers']['deleteAllIndices']; + + before(async () => { + ({ + settings: { + api: { getIndexSettings, updateIndexSettings }, + }, + indices: { + helpers: { createIndex, deleteAllIndices }, + }, + } = indexManagementService); + }); + + after(async () => await deleteAllIndices()); + + it('should fetch an index settings', async () => { + const index = await createIndex(); + + const { body } = await getIndexSettings(index).expect(200); + + // Verify we fetch the corret index settings + expect(body.settings.index.provided_name).to.be(index); + + const expectedSettings = [ + 'max_inner_result_window', + 'unassigned', + 'max_terms_count', + 'lifecycle', + 'routing_partition_size', + 'max_docvalue_fields_search', + 'merge', + 'max_refresh_listeners', + 'max_regex_length', + 'load_fixed_bitset_filters_eagerly', + 'number_of_routing_shards', + 'write', + 'verified_before_close', + 'mapping', + 'source_only', + 'soft_deletes', + 'max_script_fields', + 'query', + 'format', + 'frozen', + 'sort', + 'priority', + 'codec', + 'max_rescore_window', + 'analyze', + 'gc_deletes', + 'max_ngram_diff', + 'translog', + 'auto_expand_replicas', + 'requests', + 'data_path', + 'highlight', + 'routing', + 'search', + 'fielddata', + 'default_pipeline', + 'max_slices_per_scroll', + 'shard', + 'xpack', + 'percolator', + 'allocation', + 'refresh_interval', + 'indexing', + 'compound_format', + 'blocks', + 'max_result_window', + 'store', + 'queries', + 'warmer', + 'max_shingle_diff', + 'query_string', + ]; + + // Make sure none of the settings have been removed from ES API + expectedSettings.forEach((setting) => { + try { + expect(body.defaults.index.hasOwnProperty(setting)).to.be(true); + } catch { + throw new Error(`Expected setting "${setting}" not found.`); + } + }); + }); + + it('should update an index settings', async () => { + const index = await createIndex(); + + const { body: body1 } = await getIndexSettings(index); + expect(body1.settings.index.number_of_replicas).to.be('1'); + + const settings = { + index: { + number_of_replicas: 2, + }, + }; + await updateIndexSettings(index, settings); + + const { body: body2 } = await getIndexSettings(index); + expect(body2.settings.index.number_of_replicas).to.be('2'); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/index.ts new file mode 100644 index 0000000000000..b3316533be703 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Painless Lab API', function () { + this.tags(['esGate']); + + loadTestFile(require.resolve('./painless_lab')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts new file mode 100644 index 0000000000000..1d0d5cefedb86 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const API_BASE_PATH = '/api/painless_lab'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const svlCommonApi = getService('svlCommonApi'); + + describe('Painless Lab Routes', function () { + describe('Execute', () => { + it('should execute a valid painless script', async () => { + const script = + '"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; + + const { body } = await supertest + .post(`${API_BASE_PATH}/execute`) + .set(svlCommonApi.getInternalRequestHeader()) + .set('Content-Type', 'application/json;charset=UTF-8') + .send(script) + .expect(200); + + expect(body).to.eql({ + result: 'true', + }); + }); + + it('should return error response for invalid painless script', async () => { + const invalidScript = + '"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; + + const { body } = await supertest + .post(`${API_BASE_PATH}/execute`) + .set(svlCommonApi.getInternalRequestHeader()) + .set('Content-Type', 'application/json;charset=UTF-8') + .send(invalidScript) + .expect(200); + + expect(body.error).to.not.be(undefined); + expect(body.error.reason).to.eql('compile error'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts index 0fb2686052760..abb6ff6355464 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts @@ -26,6 +26,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../common/search_xpack'), require.resolve('../../common/core'), require.resolve('../../common/reporting'), + require.resolve('../../common/painless_lab'), ], junit: { reportName: 'Serverless Observability API Integration Tests - Common Group 1', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts index 2b7fc3653c3b6..055f6c1a17a54 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts @@ -6,7 +6,6 @@ */ import { cleanup, generate } from '@kbn/infra-forge'; -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -89,7 +88,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.5], timeSize: 5, @@ -184,7 +182,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>', threshold: [0.5], timeSize: 5, diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts index 4f1e9a4ab080b..098f4659fdd2d 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -75,7 +74,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.5], timeSize: 5, @@ -170,7 +168,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>', threshold: [0.5], timeSize: 5, diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index 47ed54412296d..0c3f43b29fdd0 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -12,7 +12,6 @@ */ import { cleanup, generate } from '@kbn/infra-forge'; -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -91,7 +90,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.9], timeSize: 1, @@ -188,7 +186,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT, threshold: [0.9], timeSize: 1, diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts index 0f47627be5e2b..453da41b81196 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts @@ -6,7 +6,6 @@ */ import { cleanup, generate } from '@kbn/infra-forge'; -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { Aggregators, Comparator, @@ -85,9 +84,8 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, - comparator: Comparator.GT, - threshold: [2], + comparator: Comparator.OUTSIDE_RANGE, + threshold: [1, 2], timeSize: 1, timeUnit: 'm', metrics: [{ name: 'A', filter: '', aggType: Aggregators.COUNT }], @@ -178,9 +176,8 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', - comparator: '>', - threshold: [2], + comparator: Comparator.OUTSIDE_RANGE, + threshold: [1, 2], timeSize: 1, timeUnit: 'm', metrics: [{ name: 'A', filter: '', aggType: 'count' }], diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts index 78daabb1eb131..0f7b7a019cc83 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts @@ -11,7 +11,6 @@ * 2.0. */ -import { CUSTOM_AGGREGATOR } from '@kbn/observability-plugin/common/custom_threshold_rule/constants'; import { kbnTestConfig } from '@kbn/test'; import moment from 'moment'; import { cleanup, generate } from '@kbn/infra-forge'; @@ -95,7 +94,6 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - aggType: CUSTOM_AGGREGATOR, comparator: Comparator.GT_OR_EQ, threshold: [0.2], timeSize: 1, @@ -205,7 +203,6 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - aggType: 'custom', comparator: '>=', threshold: [0.2], timeSize: 1, @@ -235,8 +232,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.hits.hits[0]._source?.ruleType).eql('observability.rules.custom_threshold'); expect(resp.hits.hits[0]._source?.alertDetailsUrl).eql( - // Added the S to protocol.getUrlParts as not returning the correct value. - `${protocol}s://${hostname}:${port}/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` + `${protocol}://${hostname}:${port}/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( `Average system.cpu.total.norm.pct is 80%, above the threshold of 20%. (duration: 1 min, data view: ${DATE_VIEW}, group: host-0)` diff --git a/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts index cf9314ce8201e..606924bb4a361 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts @@ -26,6 +26,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../../common/search_xpack'), require.resolve('../../common/core'), require.resolve('../../common/reporting'), + require.resolve('../../common/painless_lab'), ], junit: { reportName: 'Serverless Security API Integration Tests - Common Group 1', diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts index 672ec3aeada49..a9de87ca60d71 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_landing_page.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export function SvlSearchLandingPageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); + const browser = getService('browser'); return { async assertSvlSearchSideNavExists() { @@ -75,5 +76,15 @@ export function SvlSearchLandingPageProvider({ getService }: FtrProviderContext) }); }, }, + pipeline: { + async click() { + await testSubjects.click('create-a-pipeline-button'); + }, + async expectNavigateToCreatePipelinePage() { + expect(await browser.getCurrentUrl()).contain( + '/app/management/ingest/ingest_pipelines/create' + ); + }, + }, }; } diff --git a/x-pack/test_serverless/functional/services/ml/observability_navigation.ts b/x-pack/test_serverless/functional/services/ml/observability_navigation.ts index 3dd18587a9140..91c149dab37ac 100644 --- a/x-pack/test_serverless/functional/services/ml/observability_navigation.ts +++ b/x-pack/test_serverless/functional/services/ml/observability_navigation.ts @@ -16,10 +16,10 @@ export function MachineLearningNavigationProviderObservability({ async function navigateToArea(id: string) { await svlCommonNavigation.sidenav.openSection('observability_project_nav.aiops'); - await testSubjects.existOrFail(`~nav-item-id-observability_project_nav.aiops.ml:${id}`, { + await testSubjects.existOrFail(`~nav-item-observability_project_nav.aiops.ml:${id}`, { timeout: 60 * 1000, }); - await testSubjects.click(`~nav-item-id-observability_project_nav.aiops.ml:${id}`); + await testSubjects.click(`~nav-item-observability_project_nav.aiops.ml:${id}`); } return { diff --git a/x-pack/test_serverless/functional/test_suites/search/landing_page.ts b/x-pack/test_serverless/functional/test_suites/search/landing_page.ts index ee7fa320cc885..c15b88d5cb3e3 100644 --- a/x-pack/test_serverless/functional/test_suites/search/landing_page.ts +++ b/x-pack/test_serverless/functional/test_suites/search/landing_page.ts @@ -81,5 +81,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlSearchLandingPage.apiKeys.createApiKeyCancel(); }); }); + + describe('Pipeline creation', async () => { + it('can redirect to the pipeline creation index page', async () => { + await pageObjects.svlSearchLandingPage.pipeline.click(); + await pageObjects.svlSearchLandingPage.pipeline.expectNavigateToCreatePipelinePage(); + }); + }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/security/ml/data_frame_analytics_jobs_list.ts b/x-pack/test_serverless/functional/test_suites/security/ml/data_frame_analytics_jobs_list.ts index f0dbed0ca9a7e..3a4153b264cc6 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ml/data_frame_analytics_jobs_list.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ml/data_frame_analytics_jobs_list.ts @@ -20,7 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.svlCommonPage.login(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_ihp_outlier', '@timestamp'); await ml.api.createDataFrameAnalyticsJob( ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(dfaJobId) diff --git a/x-pack/test_serverless/shared/config.base.ts b/x-pack/test_serverless/shared/config.base.ts index ec11099f946c0..6dee26203b532 100644 --- a/x-pack/test_serverless/shared/config.base.ts +++ b/x-pack/test_serverless/shared/config.base.ts @@ -16,15 +16,16 @@ import { kibanaTestSuperuserServerless, getDockerFileMountPath, } from '@kbn/test'; -import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; +import { CA_CERT_PATH, kibanaDevServiceAccount } from '@kbn/dev-utils'; import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; +import { MOCK_IDP_REALM_NAME } from '@kbn/mock-idp-plugin/common'; import { services } from './services'; export default async () => { const servers = { kibana: { ...kbnTestConfig.getUrlParts(kibanaTestSuperuserServerless), - protocol: 'https', + protocol: process.env.TEST_CLOUD ? 'https' : 'http', certificateAuthorities: process.env.TEST_CLOUD ? undefined : [Fs.readFileSync(CA_CERT_PATH)], }, elasticsearch: { @@ -68,16 +69,6 @@ export default async () => { 'xpack.security.authc.realms.jwt.jwt1.order=-98', `xpack.security.authc.realms.jwt.jwt1.pkc_jwkset_path=${getDockerFileMountPath(jwksPath)}`, `xpack.security.authc.realms.jwt.jwt1.token_type=access_token`, - - 'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7', - 'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1', - 'xpack.security.authc.realms.saml.cloud-saml-kibana.order=101', - `xpack.security.authc.realms.saml.cloud-saml-kibana.idp.metadata.path=${getDockerFileMountPath( - idpPath - )}`, - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${servers.kibana.port}/api/security/saml/callback`, - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://localhost:${servers.kibana.port}`, - `xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://localhost:${servers.kibana.port}/logout`, ], ssl: true, // SSL is required for SAML realm }, @@ -89,10 +80,6 @@ export default async () => { }, sourceArgs: ['--no-base-path', '--env.name=development'], serverArgs: [ - '--server.ssl.enabled=true', - `--server.ssl.key=${KBN_KEY_PATH}`, - `--server.ssl.certificate=${KBN_CERT_PATH}`, - `--server.ssl.certificateAuthorities=${CA_CERT_PATH}`, `--server.restrictInternalApis=true`, `--server.port=${servers.kibana.port}`, '--status.allowAnonymous=true', @@ -147,7 +134,7 @@ export default async () => { // user navigates to `/login` page directly and enters username and password in the login form. '--xpack.security.authc.selector.enabled=false', `--xpack.security.authc.providers=${JSON.stringify({ - saml: { 'cloud-saml-kibana': { order: 0, realm: 'cloud-saml-kibana' } }, + saml: { 'cloud-saml-kibana': { order: 0, realm: MOCK_IDP_REALM_NAME } }, basic: { 'cloud-basic': { order: 1 } }, })}`, '--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', diff --git a/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml b/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml index a8f7adfc85777..8b0627d960a49 100644 --- a/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml +++ b/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml @@ -240,6 +240,7 @@ t3_analyst: privileges: - read - write + - maintenance - names: - .lists* - .items* @@ -293,12 +294,16 @@ threat_intelligence_analyst: - endgame-* - filebeat-* - logs-* - - .lists* - - .items* - packetbeat-* - winlogbeat-* privileges: - read + - names: + - .lists* + - .items* + privileges: + - read + - write - names: - .alerts-security* - .siem-signals-* @@ -317,8 +322,7 @@ threat_intelligence_analyst: - application: "kibana-.kibana" privileges: - feature_ml.read - - feature_siem.read - - feature_siem.read_alerts + - feature_siem.all - feature_siem.endpoint_list_read - feature_siem.blocklist_all - feature_securitySolutionCases.all @@ -603,6 +607,7 @@ endpoint_operations_analyst: privileges: - read - write + - maintenance applications: - application: "kibana-.kibana" privileges: @@ -653,11 +658,15 @@ endpoint_policy_manager: - logs-* - packetbeat-* - winlogbeat-* + - risk-score.risk-score-* + privileges: + - read + - names: - .lists* - .items* - - risk-score.risk-score-* privileges: - read + - write - names: - .alerts-security* - .siem-signals-* diff --git a/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts b/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts index 4721a6f71d10e..ce922732796fe 100644 --- a/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts +++ b/x-pack/test_serverless/shared/services/deployment_agnostic_services.ts @@ -21,6 +21,7 @@ const deploymentAgnosticApiIntegrationServices = _.pick(apiIntegrationServices, 'esSupertest', 'indexPatterns', 'ingestPipelines', + 'indexManagement', 'kibanaServer', 'ml', 'randomness', diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index e825bff536d8e..af6f6e4b25e99 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -48,7 +48,6 @@ "@kbn/rison", "@kbn/discover-plugin", "@kbn/data-plugin", - "@kbn/security-api-integration-helpers", "@kbn/std", "@kbn/data-view-field-editor-plugin", "@kbn/data-plugin", @@ -68,5 +67,7 @@ "@kbn/apm-synthtrace", "@kbn/apm-synthtrace-client", "@kbn/reporting-export-types-csv-common", + "@kbn/mock-idp-plugin", + "@kbn/index-management-plugin", ] } diff --git a/yarn.lock b/yarn.lock index a3a29ac19c070..2e609e33d4ae3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1627,10 +1627,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@90.0.0": - version "90.0.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-90.0.0.tgz#f105576513ed2fb31fefab5e1287be4e090ba0f3" - integrity sha512-4LXY4CwXU3FHeOiW76VPTaYcPVKEiqpb8GGHFEkYfBPWBgXyUJUVJyXwX9k8pzXElxM9I68OTDVRxIUzEzFiug== +"@elastic/eui@90.0.1": + version "90.0.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-90.0.1.tgz#f70391559a113d6df4622f5c0160fb94dc064885" + integrity sha512-KMr3blXQUBQjCjb4hskKaNMxLwJOOLSOwAvoZQjqj3ZxQS9yinZ1Ly8mn16ua8iC6HVra1GB0lsNyrCD+VlgwA== dependencies: "@hello-pangea/dnd" "^16.3.0" "@types/lodash" "^4.14.198" @@ -3145,6 +3145,10 @@ version "0.0.0" uid "" +"@kbn/calculate-width-from-char-count@link:packages/kbn-calculate-width-from-char-count": + version "0.0.0" + uid "" + "@kbn/canvas-plugin@link:x-pack/plugins/canvas": version "0.0.0" uid "" @@ -4989,6 +4993,10 @@ version "0.0.0" uid "" +"@kbn/ml-creation-wizard-utils@link:x-pack/packages/ml/creation_wizard_utils": + version "0.0.0" + uid "" + "@kbn/ml-data-frame-analytics-utils@link:x-pack/packages/ml/data_frame_analytics_utils": version "0.0.0" uid "" @@ -4997,6 +5005,10 @@ version "0.0.0" uid "" +"@kbn/ml-data-view-utils@link:x-pack/packages/ml/data_view_utils": + version "0.0.0" + uid "" + "@kbn/ml-date-picker@link:x-pack/packages/ml/date_picker": version "0.0.0" uid "" @@ -5077,6 +5089,10 @@ version "0.0.0" uid "" +"@kbn/mock-idp-plugin@link:packages/kbn-mock-idp-plugin": + version "0.0.0" + uid "" + "@kbn/monaco@link:packages/kbn-monaco": version "0.0.0" uid "" @@ -5153,6 +5169,10 @@ version "0.0.0" uid "" +"@kbn/openapi-bundler@link:packages/kbn-openapi-bundler": + version "0.0.0" + uid "" + "@kbn/openapi-generator@link:packages/kbn-openapi-generator": version "0.0.0" uid "" @@ -5181,6 +5201,10 @@ version "0.0.0" uid "" +"@kbn/panel-loader@link:packages/kbn-panel-loader": + version "0.0.0" + uid "" + "@kbn/peggy-loader@link:packages/kbn-peggy-loader": version "0.0.0" uid "" @@ -5489,6 +5513,18 @@ version "0.0.0" uid "" +"@kbn/security-plugin-types-common@link:x-pack/packages/security/plugin_types_common": + version "0.0.0" + uid "" + +"@kbn/security-plugin-types-public@link:x-pack/packages/security/plugin_types_public": + version "0.0.0" + uid "" + +"@kbn/security-plugin-types-server@link:x-pack/packages/security/plugin_types_server": + version "0.0.0" + uid "" + "@kbn/security-plugin@link:x-pack/plugins/security": version "0.0.0" uid "" @@ -5933,10 +5969,6 @@ version "0.0.0" uid "" -"@kbn/subscription-tracking@link:packages/kbn-subscription-tracking": - version "0.0.0" - uid "" - "@kbn/synthetics-plugin@link:x-pack/plugins/synthetics": version "0.0.0" uid "" @@ -6673,6 +6705,17 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/agent@^2.0.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.0.tgz#e81f00fdb2a670750ff7731bbefb47ecbf0ccf44" + integrity sha512-2yThA1Es98orMkpSLVqlDZAMPK3jHJhifP2gnNUdk1754uZ8yI5c+ulCoVG+WlntQA6MzhrURMXjSd9Z7dJ2/Q== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.1" + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -10678,11 +10721,16 @@ abab@^2.0.4, abab@^2.0.6: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -abbrev@1, abbrev@^1.0.0: +abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -12458,16 +12506,16 @@ cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" -cacache@^17.0.0: - version "17.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044" - integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg== +cacache@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.0.tgz#17a9ecd6e1be2564ebe6cdca5f7cfed2bfeb6ddc" + integrity sha512-I7mVOPl3PUCeRub1U8YoGz2Lqv9WOBpobZ8RyWFXmReuILz+3OAyTa5oH3QPdtKZD7N0Yk00aLfzn0qvp8dZ1w== dependencies: "@npmcli/fs" "^3.1.0" fs-minipass "^3.0.0" glob "^10.2.2" - lru-cache "^7.7.1" - minipass "^5.0.0" + lru-cache "^10.0.1" + minipass "^7.0.3" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" @@ -17553,16 +17601,16 @@ glob@8.1.0, glob@^8.0.1, glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" -glob@^10.2.2: - version "10.2.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.7.tgz#9dd2828cd5bc7bd861e7738d91e7113dda41d7d8" - integrity sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA== +glob@^10.2.2, glob@^10.3.10: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== dependencies: foreground-child "^3.1.0" - jackspeak "^2.0.3" + jackspeak "^2.3.5" minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2" - path-scurry "^1.7.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0, glob@^7.2.3, glob@~7.2.0: version "7.2.3" @@ -18526,7 +18574,7 @@ https-proxy-agent@^6.1.0: agent-base "^7.0.2" debug "4" -https-proxy-agent@^7.0.2: +https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== @@ -18778,10 +18826,10 @@ inquirer@^8.2.5: through "^2.3.6" wrap-ansi "^7.0.0" -install-artifact-from-github@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.3.tgz#57d89bacfa0f47d7307fe41b6247cda9f9a8079c" - integrity sha512-x79SL0d8WOi1ZjXSTUqqs0GPQZ92YArJAN9O46wgU9wdH2U9ecyyhB9YGDbPe2OLV4ptmt6AZYRQZ2GydQZosQ== +install-artifact-from-github@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.5.tgz#88c96fe40e5eb21d45586d564208c648a1dbf38d" + integrity sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg== internal-slot@^1.0.3, internal-slot@^1.0.5: version "1.0.5" @@ -19529,6 +19577,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + isnumber@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isnumber/-/isnumber-1.0.0.tgz#0e3f9759b581d99dd85086f0ec2a74909cfadd01" @@ -19667,10 +19720,10 @@ its-name@1.0.0: resolved "https://registry.yarnpkg.com/its-name/-/its-name-1.0.0.tgz#2065f1883ecb568c65f7112ddbf123401fae4af0" integrity sha1-IGXxiD7LVoxl9xEt2/EjQB+uSvA= -jackspeak@^2.0.3: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6" - integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw== +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: @@ -20704,10 +20757,10 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -langchain@^0.0.151: - version "0.0.151" - resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.0.151.tgz#10c1ebdda0d772e49dbca755ada6307c9280f601" - integrity sha512-RA7/ELK5dqUgv5glIP5Wm5JmbnrjH/eeROYdKGDGaDUNZrRJ2CLuEu+oJH7hcE5hpPoPlkLBCs/vz4hvr/YtYw== +langchain@^0.0.186: + version "0.0.186" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.0.186.tgz#59753972764d7ee4cf3e00dca1d74c95659bccbd" + integrity sha512-uXDipmw9aUrUmDNcFr2XH9ORmshWIlIb/qFKneS1K3X5upMUg7TSbaBxqV9WxuuenLUSYaoTcTy7P/pKkbqXPg== dependencies: "@anthropic-ai/sdk" "^0.6.2" ansi-styles "^5.0.0" @@ -20720,16 +20773,15 @@ langchain@^0.0.151: js-yaml "^4.1.0" jsonpointer "^5.0.1" langchainhub "~0.0.6" - langsmith "~0.0.31" + langsmith "~0.0.48" ml-distance "^4.0.0" - object-hash "^3.0.0" - openai "~4.4.0" + openai "^4.17.0" openapi-types "^12.1.3" p-queue "^6.6.2" p-retry "4" uuid "^9.0.0" yaml "^2.2.1" - zod "^3.21.4" + zod "^3.22.3" zod-to-json-schema "^3.20.4" langchainhub@~0.0.6: @@ -20737,10 +20789,10 @@ langchainhub@~0.0.6: resolved "https://registry.yarnpkg.com/langchainhub/-/langchainhub-0.0.6.tgz#9d2d06e4ce0807b4e8a31e19611f57aef990b54d" integrity sha512-SW6105T+YP1cTe0yMf//7kyshCgvCTyFBMTgH2H3s9rTAR4e+78DA/BBrUL/Mt4Q5eMWui7iGuAYb3pgGsdQ9w== -langsmith@~0.0.31: - version "0.0.33" - resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.0.33.tgz#0b8b0a7b9981777f37df86748892e417bdf94aea" - integrity sha512-8dVBjJsuIwsnUFtA6OJ85k2wWzpka+LsF2EFzpzpF3yOHO/Ui7oeCMobyp6L7QcgWIBdRUIJY6sNSxAW0uAMHg== +langsmith@^0.0.48, langsmith@~0.0.48: + version "0.0.48" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.0.48.tgz#3a9a8ce257271ddb43d01ebf585c4370a3a3ba79" + integrity sha512-s0hW8iZ90Q9XLTnDK0Pgee245URV3b1cXQjPDj5OKm1+KN7iSK1pKx+4CO7RcFLz58Ixe7Mt+mVcomYqUuryxQ== dependencies: "@types/uuid" "^9.0.1" commander "^10.0.1" @@ -21335,10 +21387,10 @@ lowlight@^1.14.0: fault "^1.0.0" highlight.js "~10.4.0" -lru-cache@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== +lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" + integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== lru-cache@^4.0.0, lru-cache@^4.1.5: version "4.1.5" @@ -21367,11 +21419,6 @@ lru-cache@^7.14.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -lru-cache@^9.1.1: - version "9.1.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" - integrity sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ== - lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -21448,25 +21495,21 @@ make-fetch-happen@^10.0.4: socks-proxy-agent "^7.0.0" ssri "^9.0.0" -make-fetch-happen@^11.0.3: - version "11.1.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" - integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== +make-fetch-happen@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz#705d6f6cbd7faecb8eac2432f551e49475bfedf0" + integrity sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A== dependencies: - agentkeepalive "^4.2.1" - cacache "^17.0.0" + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" http-cache-semantics "^4.1.1" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^5.0.0" + minipass "^7.0.2" minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^0.6.3" promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" ssri "^10.0.0" make-fetch-happen@^9.1.0: @@ -22212,10 +22255,10 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" - integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" @@ -22592,10 +22635,10 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.17.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.17.0, nan@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== nano-css@^5.2.1: version "5.2.1" @@ -22824,33 +22867,32 @@ node-gyp-build@^4.2.2, node-gyp-build@^4.2.3, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== -node-gyp@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== +node-gyp@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.0.1.tgz#205514fc19e5830fa991e4a689f9e81af377a966" + integrity sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg== dependencies: env-paths "^2.2.0" - glob "^7.1.4" + exponential-backoff "^3.1.1" + glob "^10.3.10" graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^3.0.0" semver "^7.3.5" tar "^6.1.2" - which "^2.0.2" + which "^4.0.0" -node-gyp@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" - integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== +node-gyp@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== dependencies: env-paths "^2.2.0" - exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^11.0.3" - nopt "^6.0.0" + make-fetch-happen "^9.1.0" + nopt "^5.0.0" npmlog "^6.0.0" rimraf "^3.0.2" semver "^7.3.5" @@ -22965,12 +23007,12 @@ nopt@^5.0.0: dependencies: abbrev "1" -nopt@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" - integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== +nopt@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== dependencies: - abbrev "^1.0.0" + abbrev "^2.0.0" normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" @@ -23162,11 +23204,6 @@ object-hash@^1.3.0, object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== -object-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" - integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== - object-identity-map@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/object-identity-map/-/object-identity-map-1.0.2.tgz#2b4213a4285ca3a8cd2e696782c9964f887524e7" @@ -23373,10 +23410,10 @@ openai@^3.3.0: axios "^0.26.0" form-data "^4.0.0" -openai@~4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/openai/-/openai-4.4.0.tgz#dbaab326eb044ddec479951b245850c482678031" - integrity sha512-JN0t628Kh95T0IrXl0HdBqnlJg+4Vq0Bnh55tio+dfCnyzHvMLiWyCM9m726MAJD2YkDU4/8RQB6rNbEq9ct2w== +openai@^4.17.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.17.5.tgz#096655741965656ec969731e97d4bce880112d66" + integrity sha512-SDgA933/QOjISCgWRc/JQhY1HweYZ6FOie3bWrCpj09FA5xIlaomldbyzICHNjtkh7SWEmGYFjRHIDtuwr+eTw== dependencies: "@types/node" "^18.11.18" "@types/node-fetch" "^2.6.4" @@ -23386,6 +23423,7 @@ openai@~4.4.0: form-data-encoder "1.7.2" formdata-node "^4.3.2" node-fetch "^2.6.7" + web-streams-polyfill "^3.2.1" openapi-types@^10.0.0: version "10.0.0" @@ -23904,13 +23942,13 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.7.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.9.2.tgz#90f9d296ac5e37e608028e28a447b11d385b3f63" - integrity sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== dependencies: - lru-cache "^9.1.1" - minipass "^5.0.0 || ^6.0.2" + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-to-regexp@0.1.7: version "0.1.7" @@ -24730,6 +24768,11 @@ prismjs@^1.22.0, prismjs@~1.27.0: resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -25246,14 +25289,14 @@ re-resizable@^6.9.9: resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216" integrity sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA== -re2@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/re2/-/re2-1.20.1.tgz#27450f6de6299635f50305a670cc15ac30687f85" - integrity sha512-JbzIoI5adNCqGUK8wHG1dMSyggvPyA4kx2hewt1lma5sP7/iWCfM15XKbCZlX2yvu5k80jSKAOQqJF7KC+2n8Q== +re2@1.20.9: + version "1.20.9" + resolved "https://registry.yarnpkg.com/re2/-/re2-1.20.9.tgz#3e6e5b73cdd911cdbdfe5133cc6670600e33871b" + integrity sha512-ZYcPTFr5ha2xq3WQjBDTF9CWPSDK1z28MLh5UFRxc//7X8BNQ3A7yR7ITnP0jO346661ertdKVFqw1qoL3FMEQ== dependencies: - install-artifact-from-github "^1.3.3" - nan "^2.17.0" - node-gyp "^9.4.0" + install-artifact-from-github "^1.3.5" + nan "^2.18.0" + node-gyp "^10.0.1" react-ace@^7.0.5: version "7.0.5" @@ -27453,7 +27496,7 @@ socks-proxy-agent@^7.0.0: debug "^4.3.3" socks "^2.6.2" -socks-proxy-agent@^8.0.2: +socks-proxy-agent@^8.0.1, socks-proxy-agent@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== @@ -30891,6 +30934,13 @@ which@^3.0.1: dependencies: isexe "^2.0.0" +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -31365,7 +31415,7 @@ zod-to-json-schema@^3.20.4: resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.21.4.tgz#de97c5b6d4a25e9d444618486cb55c0c7fb949fd" integrity sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw== -zod@^3.21.4, zod@^3.22.3: +zod@^3.22.3: version "3.22.3" resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.3.tgz#2fbc96118b174290d94e8896371c95629e87a060" integrity sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==